




  subroutine fiona_output_0d_i2(dataset_name, var_name, value, start)

    character(*), intent(in) :: dataset_name
    character(*), intent(in) :: var_name
    integer(2), intent(in) :: value
    integer, intent(in), optional :: start(:)

    type(dataset_type), pointer :: dataset
    type(var_type), pointer :: var
    integer ierr
    integer, allocatable :: start_opt(:)

    dataset => get_dataset(dataset_name)
    var => dataset%get_var(var_name)

    if (dataset%is_group_root) then
      if (var%dims(1)%ptr%size == NF90_UNLIMITED) then
        if (present(start)) then
          allocate(start_opt(size(start)+1)); start_opt(1:size(start)) = start; start_opt(size(start)+1) = dataset%time_step
        else
          allocate(start_opt(1)); start_opt(1) = dataset%time_step
        end if
      else
        if (present(start)) then
          allocate(start_opt(size(start))); start_opt = start
        else
          allocate(start_opt(1)); start_opt(1) = 1
        end if
      end if
#ifdef HAS_MPI
      if (dataset%is_parallel) then
        if (dataset%time_dim%size == NF90_UNLIMITED) then
          ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_COLLECTIVE)
        else
          ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_INDEPENDENT)
        end if
        call handle_error(ierr, "Failed to set parallel access for variable " // &
                          trim(var%name) // "!", __FILE__, __LINE__)
      end if
#endif
      ierr = NF90_PUT_VAR(dataset%id, var%id, value, start=start_opt)
      call handle_error(ierr, "Failed to write variable " // trim(var%name) // &
                        " in dataset " // trim(dataset%name) // "!", __FILE__, __LINE__)
    end if

    if (allocated(start_opt)) deallocate(start_opt)

  end subroutine fiona_output_0d_i2

  subroutine fiona_output_0d_i4(dataset_name, var_name, value, start)

    character(*), intent(in) :: dataset_name
    character(*), intent(in) :: var_name
    integer(4), intent(in) :: value
    integer, intent(in), optional :: start(:)

    type(dataset_type), pointer :: dataset
    type(var_type), pointer :: var
    integer ierr
    integer, allocatable :: start_opt(:)

    dataset => get_dataset(dataset_name)
    var => dataset%get_var(var_name)

    if (dataset%is_group_root) then
      if (var%dims(1)%ptr%size == NF90_UNLIMITED) then
        if (present(start)) then
          allocate(start_opt(size(start)+1)); start_opt(1:size(start)) = start; start_opt(size(start)+1) = dataset%time_step
        else
          allocate(start_opt(1)); start_opt(1) = dataset%time_step
        end if
      else
        if (present(start)) then
          allocate(start_opt(size(start))); start_opt = start
        else
          allocate(start_opt(1)); start_opt(1) = 1
        end if
      end if
#ifdef HAS_MPI
      if (dataset%is_parallel) then
        if (dataset%time_dim%size == NF90_UNLIMITED) then
          ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_COLLECTIVE)
        else
          ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_INDEPENDENT)
        end if
        call handle_error(ierr, "Failed to set parallel access for variable " // &
                          trim(var%name) // "!", __FILE__, __LINE__)
      end if
#endif
      ierr = NF90_PUT_VAR(dataset%id, var%id, value, start=start_opt)
      call handle_error(ierr, "Failed to write variable " // trim(var%name) // &
                        " in dataset " // trim(dataset%name) // "!", __FILE__, __LINE__)
    end if

    if (allocated(start_opt)) deallocate(start_opt)

  end subroutine fiona_output_0d_i4

  subroutine fiona_output_0d_r4(dataset_name, var_name, value, start)

    character(*), intent(in) :: dataset_name
    character(*), intent(in) :: var_name
    real(4), intent(in) :: value
    integer, intent(in), optional :: start(:)

    type(dataset_type), pointer :: dataset
    type(var_type), pointer :: var
    integer ierr
    integer, allocatable :: start_opt(:)

    dataset => get_dataset(dataset_name)
    var => dataset%get_var(var_name)

    if (dataset%is_group_root) then
      if (var%dims(1)%ptr%size == NF90_UNLIMITED) then
        if (present(start)) then
          allocate(start_opt(size(start)+1)); start_opt(1:size(start)) = start; start_opt(size(start)+1) = dataset%time_step
        else
          allocate(start_opt(1)); start_opt(1) = dataset%time_step
        end if
      else
        if (present(start)) then
          allocate(start_opt(size(start))); start_opt = start
        else
          allocate(start_opt(1)); start_opt(1) = 1
        end if
      end if
#ifdef HAS_MPI
      if (dataset%is_parallel) then
        if (dataset%time_dim%size == NF90_UNLIMITED) then
          ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_COLLECTIVE)
        else
          ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_INDEPENDENT)
        end if
        call handle_error(ierr, "Failed to set parallel access for variable " // &
                          trim(var%name) // "!", __FILE__, __LINE__)
      end if
#endif
      ierr = NF90_PUT_VAR(dataset%id, var%id, value, start=start_opt)
      call handle_error(ierr, "Failed to write variable " // trim(var%name) // &
                        " in dataset " // trim(dataset%name) // "!", __FILE__, __LINE__)
    end if

    if (allocated(start_opt)) deallocate(start_opt)

  end subroutine fiona_output_0d_r4

  subroutine fiona_output_0d_r8(dataset_name, var_name, value, start)

    character(*), intent(in) :: dataset_name
    character(*), intent(in) :: var_name
    real(8), intent(in) :: value
    integer, intent(in), optional :: start(:)

    type(dataset_type), pointer :: dataset
    type(var_type), pointer :: var
    integer ierr
    integer, allocatable :: start_opt(:)

    dataset => get_dataset(dataset_name)
    var => dataset%get_var(var_name)

    if (dataset%is_group_root) then
      if (var%dims(1)%ptr%size == NF90_UNLIMITED) then
        if (present(start)) then
          allocate(start_opt(size(start)+1)); start_opt(1:size(start)) = start; start_opt(size(start)+1) = dataset%time_step
        else
          allocate(start_opt(1)); start_opt(1) = dataset%time_step
        end if
      else
        if (present(start)) then
          allocate(start_opt(size(start))); start_opt = start
        else
          allocate(start_opt(1)); start_opt(1) = 1
        end if
      end if
#ifdef HAS_MPI
      if (dataset%is_parallel) then
        if (dataset%time_dim%size == NF90_UNLIMITED) then
          ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_COLLECTIVE)
        else
          ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_INDEPENDENT)
        end if
        call handle_error(ierr, "Failed to set parallel access for variable " // &
                          trim(var%name) // "!", __FILE__, __LINE__)
      end if
#endif
      ierr = NF90_PUT_VAR(dataset%id, var%id, value, start=start_opt)
      call handle_error(ierr, "Failed to write variable " // trim(var%name) // &
                        " in dataset " // trim(dataset%name) // "!", __FILE__, __LINE__)
    end if

    if (allocated(start_opt)) deallocate(start_opt)

  end subroutine fiona_output_0d_r8

  subroutine fiona_output_1d_i2(dataset_name, var_name, array, start, count, fatal, only_root)

    integer, parameter :: ndim = 1

    character(*), intent(in) :: dataset_name
    character(*), intent(in) :: var_name
    integer(2), intent(in) :: array(:)
    integer, intent(in), optional :: start(ndim)
    integer, intent(in), optional :: count(ndim)
    logical, intent(in), optional :: fatal
    logical, intent(in), optional :: only_root

    type(dataset_type), pointer :: dataset
    type(var_type), pointer :: var
    type(buffer_type) buffer
    logical fatal_opt
    logical only_root_opt
    integer i, ierr
    real(8) time1, time2

    fatal_opt = .false.; if (present(fatal)) fatal_opt = fatal
    only_root_opt = .false.; if (present(only_root)) only_root_opt = only_root

    dataset => get_dataset(dataset_name)
    ! if (dataset%proc_id == 0) then
    !   time1 = MPI_WTIME()
    !   print *, " - output " // trim(var_name) // ":"
    ! end if
    var => dataset%get_var(var_name)
    if (only_root_opt) then
      ierr =  NF90_PUT_VAR(dataset%id, var%id, array)
      if (fatal_opt) then
        call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                          trim(dataset%file_path) // """!", __FILE__, __LINE__)
      end if
    else
      call gather_start_count(dataset, ndim, shape(array), start, count)
      if (dataset%is_group_root) then
        call buffer%init(buffer_size(dataset, ndim), array(1))
#ifdef HAS_MPI
        if (dataset%is_parallel) then
          if (dataset%time_dim%size == NF90_UNLIMITED) then
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_COLLECTIVE)
          else
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_INDEPENDENT)
          end if
          call handle_error(ierr, "Failed to set parallel access for variable " // trim(var%name) // "!", __FILE__, __LINE__)
        end if
#endif
      end if
      do i = 1, dataset%group_nproc
        if (dataset%is_group_root .or. dataset%group_proc_id == i - 1) then
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          if (dataset%is_group_root) then
            call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          end if
          if (dataset%is_group_root) then
            if (i == 1) then
              buffer%i2(buffer%is:buffer%ie) = reshape(array, [size(array)])
            else
#ifdef HAS_MPI
              call MPI_IRECV(buffer%i2(buffer%is:buffer%ie), buffer%ie-buffer%is+1, MPI_SHORT, i-1, 88, &
                             dataset%group_mpi_comm, dataset%group_recv_req(i), ierr)
#endif
            end if
          else
#ifdef HAS_MPI
            call MPI_SEND(array, size(array), MPI_SHORT, 0, 88, dataset%group_mpi_comm, ierr)
#endif
          end if
        end if
      end do
      ! Write data into file.
      call buffer%shift_index_back()
      if (dataset%is_group_root) then
        do i = 1, dataset%group_nproc
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          if (i > 1) call dataset%wait_recv(i)
          if (var%has_unlimited_dim) then
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%i2(buffer%is:buffer%ie), &
              start=[dataset%group_start(1:ndim,i),dataset%time_step], &
              count=[dataset%group_count(1:ndim,i),1])
          else
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%i2(buffer%is:buffer%ie), &
              start=dataset%group_start(1:ndim,i), &
              count=dataset%group_count(1:ndim,i))
          end if
          if (fatal_opt) then
            call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                              trim(dataset%file_path) // """!", __FILE__, __LINE__)
          end if
        end do
      end if
      call buffer%clear()
    end if
    ! if (dataset%proc_id == 0) then
    !   time2 = MPI_WTIME()
    !   print *, "   - write cost ", time2 - time1
    ! end if

  end subroutine fiona_output_1d_i2

  subroutine fiona_output_1d_i4(dataset_name, var_name, array, start, count, fatal, only_root)

    integer, parameter :: ndim = 1

    character(*), intent(in) :: dataset_name
    character(*), intent(in) :: var_name
    integer(4), intent(in) :: array(:)
    integer, intent(in), optional :: start(ndim)
    integer, intent(in), optional :: count(ndim)
    logical, intent(in), optional :: fatal
    logical, intent(in), optional :: only_root

    type(dataset_type), pointer :: dataset
    type(var_type), pointer :: var
    type(buffer_type) buffer
    logical fatal_opt
    logical only_root_opt
    integer i, ierr
    real(8) time1, time2

    fatal_opt = .false.; if (present(fatal)) fatal_opt = fatal
    only_root_opt = .false.; if (present(only_root)) only_root_opt = only_root

    dataset => get_dataset(dataset_name)
    ! if (dataset%proc_id == 0) then
    !   time1 = MPI_WTIME()
    !   print *, " - output " // trim(var_name) // ":"
    ! end if
    var => dataset%get_var(var_name)
    if (only_root_opt) then
      ierr =  NF90_PUT_VAR(dataset%id, var%id, array)
      if (fatal_opt) then
        call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                          trim(dataset%file_path) // """!", __FILE__, __LINE__)
      end if
    else
      call gather_start_count(dataset, ndim, shape(array), start, count)
      if (dataset%is_group_root) then
        call buffer%init(buffer_size(dataset, ndim), array(1))
#ifdef HAS_MPI
        if (dataset%is_parallel) then
          if (dataset%time_dim%size == NF90_UNLIMITED) then
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_COLLECTIVE)
          else
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_INDEPENDENT)
          end if
          call handle_error(ierr, "Failed to set parallel access for variable " // trim(var%name) // "!", __FILE__, __LINE__)
        end if
#endif
      end if
      do i = 1, dataset%group_nproc
        if (dataset%is_group_root .or. dataset%group_proc_id == i - 1) then
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          if (dataset%is_group_root) then
            call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          end if
          if (dataset%is_group_root) then
            if (i == 1) then
              buffer%i4(buffer%is:buffer%ie) = reshape(array, [size(array)])
            else
#ifdef HAS_MPI
              call MPI_IRECV(buffer%i4(buffer%is:buffer%ie), buffer%ie-buffer%is+1, MPI_INT, i-1, 88, &
                             dataset%group_mpi_comm, dataset%group_recv_req(i), ierr)
#endif
            end if
          else
#ifdef HAS_MPI
            call MPI_SEND(array, size(array), MPI_INT, 0, 88, dataset%group_mpi_comm, ierr)
#endif
          end if
        end if
      end do
      ! Write data into file.
      call buffer%shift_index_back()
      if (dataset%is_group_root) then
        do i = 1, dataset%group_nproc
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          if (i > 1) call dataset%wait_recv(i)
          if (var%has_unlimited_dim) then
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%i4(buffer%is:buffer%ie), &
              start=[dataset%group_start(1:ndim,i),dataset%time_step], &
              count=[dataset%group_count(1:ndim,i),1])
          else
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%i4(buffer%is:buffer%ie), &
              start=dataset%group_start(1:ndim,i), &
              count=dataset%group_count(1:ndim,i))
          end if
          if (fatal_opt) then
            call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                              trim(dataset%file_path) // """!", __FILE__, __LINE__)
          end if
        end do
      end if
      call buffer%clear()
    end if
    ! if (dataset%proc_id == 0) then
    !   time2 = MPI_WTIME()
    !   print *, "   - write cost ", time2 - time1
    ! end if

  end subroutine fiona_output_1d_i4

  subroutine fiona_output_1d_r4(dataset_name, var_name, array, start, count, fatal, only_root)

    integer, parameter :: ndim = 1

    character(*), intent(in) :: dataset_name
    character(*), intent(in) :: var_name
    real(4), intent(in) :: array(:)
    integer, intent(in), optional :: start(ndim)
    integer, intent(in), optional :: count(ndim)
    logical, intent(in), optional :: fatal
    logical, intent(in), optional :: only_root

    type(dataset_type), pointer :: dataset
    type(var_type), pointer :: var
    type(buffer_type) buffer
    logical fatal_opt
    logical only_root_opt
    integer i, ierr
    real(8) time1, time2

    fatal_opt = .false.; if (present(fatal)) fatal_opt = fatal
    only_root_opt = .false.; if (present(only_root)) only_root_opt = only_root

    dataset => get_dataset(dataset_name)
    ! if (dataset%proc_id == 0) then
    !   time1 = MPI_WTIME()
    !   print *, " - output " // trim(var_name) // ":"
    ! end if
    var => dataset%get_var(var_name)
    if (only_root_opt) then
      ierr =  NF90_PUT_VAR(dataset%id, var%id, array)
      if (fatal_opt) then
        call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                          trim(dataset%file_path) // """!", __FILE__, __LINE__)
      end if
    else
      call gather_start_count(dataset, ndim, shape(array), start, count)
      if (dataset%is_group_root) then
        call buffer%init(buffer_size(dataset, ndim), array(1))
#ifdef HAS_MPI
        if (dataset%is_parallel) then
          if (dataset%time_dim%size == NF90_UNLIMITED) then
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_COLLECTIVE)
          else
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_INDEPENDENT)
          end if
          call handle_error(ierr, "Failed to set parallel access for variable " // trim(var%name) // "!", __FILE__, __LINE__)
        end if
#endif
      end if
      do i = 1, dataset%group_nproc
        if (dataset%is_group_root .or. dataset%group_proc_id == i - 1) then
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          if (dataset%is_group_root) then
            call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          end if
          if (dataset%is_group_root) then
            if (i == 1) then
              buffer%r4(buffer%is:buffer%ie) = reshape(array, [size(array)])
            else
#ifdef HAS_MPI
              call MPI_IRECV(buffer%r4(buffer%is:buffer%ie), buffer%ie-buffer%is+1, MPI_FLOAT, i-1, 88, &
                             dataset%group_mpi_comm, dataset%group_recv_req(i), ierr)
#endif
            end if
          else
#ifdef HAS_MPI
            call MPI_SEND(array, size(array), MPI_FLOAT, 0, 88, dataset%group_mpi_comm, ierr)
#endif
          end if
        end if
      end do
      ! Write data into file.
      call buffer%shift_index_back()
      if (dataset%is_group_root) then
        do i = 1, dataset%group_nproc
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          if (i > 1) call dataset%wait_recv(i)
          if (var%has_unlimited_dim) then
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%r4(buffer%is:buffer%ie), &
              start=[dataset%group_start(1:ndim,i),dataset%time_step], &
              count=[dataset%group_count(1:ndim,i),1])
          else
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%r4(buffer%is:buffer%ie), &
              start=dataset%group_start(1:ndim,i), &
              count=dataset%group_count(1:ndim,i))
          end if
          if (fatal_opt) then
            call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                              trim(dataset%file_path) // """!", __FILE__, __LINE__)
          end if
        end do
      end if
      call buffer%clear()
    end if
    ! if (dataset%proc_id == 0) then
    !   time2 = MPI_WTIME()
    !   print *, "   - write cost ", time2 - time1
    ! end if

  end subroutine fiona_output_1d_r4

  subroutine fiona_output_1d_r8(dataset_name, var_name, array, start, count, fatal, only_root)

    integer, parameter :: ndim = 1

    character(*), intent(in) :: dataset_name
    character(*), intent(in) :: var_name
    real(8), intent(in) :: array(:)
    integer, intent(in), optional :: start(ndim)
    integer, intent(in), optional :: count(ndim)
    logical, intent(in), optional :: fatal
    logical, intent(in), optional :: only_root

    type(dataset_type), pointer :: dataset
    type(var_type), pointer :: var
    type(buffer_type) buffer
    logical fatal_opt
    logical only_root_opt
    integer i, ierr
    real(8) time1, time2

    fatal_opt = .false.; if (present(fatal)) fatal_opt = fatal
    only_root_opt = .false.; if (present(only_root)) only_root_opt = only_root

    dataset => get_dataset(dataset_name)
    ! if (dataset%proc_id == 0) then
    !   time1 = MPI_WTIME()
    !   print *, " - output " // trim(var_name) // ":"
    ! end if
    var => dataset%get_var(var_name)
    if (only_root_opt) then
      ierr =  NF90_PUT_VAR(dataset%id, var%id, array)
      if (fatal_opt) then
        call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                          trim(dataset%file_path) // """!", __FILE__, __LINE__)
      end if
    else
      call gather_start_count(dataset, ndim, shape(array), start, count)
      if (dataset%is_group_root) then
        call buffer%init(buffer_size(dataset, ndim), array(1))
#ifdef HAS_MPI
        if (dataset%is_parallel) then
          if (dataset%time_dim%size == NF90_UNLIMITED) then
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_COLLECTIVE)
          else
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_INDEPENDENT)
          end if
          call handle_error(ierr, "Failed to set parallel access for variable " // trim(var%name) // "!", __FILE__, __LINE__)
        end if
#endif
      end if
      do i = 1, dataset%group_nproc
        if (dataset%is_group_root .or. dataset%group_proc_id == i - 1) then
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          if (dataset%is_group_root) then
            call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          end if
          if (dataset%is_group_root) then
            if (i == 1) then
              buffer%r8(buffer%is:buffer%ie) = reshape(array, [size(array)])
            else
#ifdef HAS_MPI
              call MPI_IRECV(buffer%r8(buffer%is:buffer%ie), buffer%ie-buffer%is+1, MPI_DOUBLE, i-1, 88, &
                             dataset%group_mpi_comm, dataset%group_recv_req(i), ierr)
#endif
            end if
          else
#ifdef HAS_MPI
            call MPI_SEND(array, size(array), MPI_DOUBLE, 0, 88, dataset%group_mpi_comm, ierr)
#endif
          end if
        end if
      end do
      ! Write data into file.
      call buffer%shift_index_back()
      if (dataset%is_group_root) then
        do i = 1, dataset%group_nproc
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          if (i > 1) call dataset%wait_recv(i)
          if (var%has_unlimited_dim) then
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%r8(buffer%is:buffer%ie), &
              start=[dataset%group_start(1:ndim,i),dataset%time_step], &
              count=[dataset%group_count(1:ndim,i),1])
          else
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%r8(buffer%is:buffer%ie), &
              start=dataset%group_start(1:ndim,i), &
              count=dataset%group_count(1:ndim,i))
          end if
          if (fatal_opt) then
            call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                              trim(dataset%file_path) // """!", __FILE__, __LINE__)
          end if
        end do
      end if
      call buffer%clear()
    end if
    ! if (dataset%proc_id == 0) then
    !   time2 = MPI_WTIME()
    !   print *, "   - write cost ", time2 - time1
    ! end if

  end subroutine fiona_output_1d_r8

  subroutine fiona_output_2d_i2(dataset_name, var_name, array, start, count, fatal, only_root)

    integer, parameter :: ndim = 2

    character(*), intent(in) :: dataset_name
    character(*), intent(in) :: var_name
    integer(2), intent(in) :: array(:,:)
    integer, intent(in), optional :: start(ndim)
    integer, intent(in), optional :: count(ndim)
    logical, intent(in), optional :: fatal
    logical, intent(in), optional :: only_root

    type(dataset_type), pointer :: dataset
    type(var_type), pointer :: var
    type(buffer_type) buffer
    logical fatal_opt
    logical only_root_opt
    integer i, ierr
    real(8) time1, time2

    fatal_opt = .false.; if (present(fatal)) fatal_opt = fatal
    only_root_opt = .false.; if (present(only_root)) only_root_opt = only_root

    dataset => get_dataset(dataset_name)
    ! if (dataset%proc_id == 0) then
    !   time1 = MPI_WTIME()
    !   print *, " - output " // trim(var_name) // ":"
    ! end if
    var => dataset%get_var(var_name)
    if (only_root_opt) then
      ierr =  NF90_PUT_VAR(dataset%id, var%id, array)
      if (fatal_opt) then
        call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                          trim(dataset%file_path) // """!", __FILE__, __LINE__)
      end if
    else
      call gather_start_count(dataset, ndim, shape(array), start, count)
      if (dataset%is_group_root) then
        call buffer%init(buffer_size(dataset, ndim), array(1,1))
#ifdef HAS_MPI
        if (dataset%is_parallel) then
          if (dataset%time_dim%size == NF90_UNLIMITED) then
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_COLLECTIVE)
          else
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_INDEPENDENT)
          end if
          call handle_error(ierr, "Failed to set parallel access for variable " // trim(var%name) // "!", __FILE__, __LINE__)
        end if
#endif
      end if
      do i = 1, dataset%group_nproc
        if (dataset%is_group_root .or. dataset%group_proc_id == i - 1) then
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          if (dataset%is_group_root) then
            call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          end if
          if (dataset%is_group_root) then
            if (i == 1) then
              buffer%i2(buffer%is:buffer%ie) = reshape(array, [size(array)])
            else
#ifdef HAS_MPI
              call MPI_IRECV(buffer%i2(buffer%is:buffer%ie), buffer%ie-buffer%is+1, MPI_SHORT, i-1, 88, &
                             dataset%group_mpi_comm, dataset%group_recv_req(i), ierr)
#endif
            end if
          else
#ifdef HAS_MPI
            call MPI_SEND(array, size(array), MPI_SHORT, 0, 88, dataset%group_mpi_comm, ierr)
#endif
          end if
        end if
      end do
      ! Write data into file.
      call buffer%shift_index_back()
      if (dataset%is_group_root) then
        do i = 1, dataset%group_nproc
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          if (i > 1) call dataset%wait_recv(i)
          if (var%has_unlimited_dim) then
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%i2(buffer%is:buffer%ie), &
              start=[dataset%group_start(1:ndim,i),dataset%time_step], &
              count=[dataset%group_count(1:ndim,i),1])
          else
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%i2(buffer%is:buffer%ie), &
              start=dataset%group_start(1:ndim,i), &
              count=dataset%group_count(1:ndim,i))
          end if
          if (fatal_opt) then
            call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                              trim(dataset%file_path) // """!", __FILE__, __LINE__)
          end if
        end do
      end if
      call buffer%clear()
    end if
    ! if (dataset%proc_id == 0) then
    !   time2 = MPI_WTIME()
    !   print *, "   - write cost ", time2 - time1
    ! end if

  end subroutine fiona_output_2d_i2

  subroutine fiona_output_2d_i4(dataset_name, var_name, array, start, count, fatal, only_root)

    integer, parameter :: ndim = 2

    character(*), intent(in) :: dataset_name
    character(*), intent(in) :: var_name
    integer(4), intent(in) :: array(:,:)
    integer, intent(in), optional :: start(ndim)
    integer, intent(in), optional :: count(ndim)
    logical, intent(in), optional :: fatal
    logical, intent(in), optional :: only_root

    type(dataset_type), pointer :: dataset
    type(var_type), pointer :: var
    type(buffer_type) buffer
    logical fatal_opt
    logical only_root_opt
    integer i, ierr
    real(8) time1, time2

    fatal_opt = .false.; if (present(fatal)) fatal_opt = fatal
    only_root_opt = .false.; if (present(only_root)) only_root_opt = only_root

    dataset => get_dataset(dataset_name)
    ! if (dataset%proc_id == 0) then
    !   time1 = MPI_WTIME()
    !   print *, " - output " // trim(var_name) // ":"
    ! end if
    var => dataset%get_var(var_name)
    if (only_root_opt) then
      ierr =  NF90_PUT_VAR(dataset%id, var%id, array)
      if (fatal_opt) then
        call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                          trim(dataset%file_path) // """!", __FILE__, __LINE__)
      end if
    else
      call gather_start_count(dataset, ndim, shape(array), start, count)
      if (dataset%is_group_root) then
        call buffer%init(buffer_size(dataset, ndim), array(1,1))
#ifdef HAS_MPI
        if (dataset%is_parallel) then
          if (dataset%time_dim%size == NF90_UNLIMITED) then
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_COLLECTIVE)
          else
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_INDEPENDENT)
          end if
          call handle_error(ierr, "Failed to set parallel access for variable " // trim(var%name) // "!", __FILE__, __LINE__)
        end if
#endif
      end if
      do i = 1, dataset%group_nproc
        if (dataset%is_group_root .or. dataset%group_proc_id == i - 1) then
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          if (dataset%is_group_root) then
            call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          end if
          if (dataset%is_group_root) then
            if (i == 1) then
              buffer%i4(buffer%is:buffer%ie) = reshape(array, [size(array)])
            else
#ifdef HAS_MPI
              call MPI_IRECV(buffer%i4(buffer%is:buffer%ie), buffer%ie-buffer%is+1, MPI_INT, i-1, 88, &
                             dataset%group_mpi_comm, dataset%group_recv_req(i), ierr)
#endif
            end if
          else
#ifdef HAS_MPI
            call MPI_SEND(array, size(array), MPI_INT, 0, 88, dataset%group_mpi_comm, ierr)
#endif
          end if
        end if
      end do
      ! Write data into file.
      call buffer%shift_index_back()
      if (dataset%is_group_root) then
        do i = 1, dataset%group_nproc
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          if (i > 1) call dataset%wait_recv(i)
          if (var%has_unlimited_dim) then
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%i4(buffer%is:buffer%ie), &
              start=[dataset%group_start(1:ndim,i),dataset%time_step], &
              count=[dataset%group_count(1:ndim,i),1])
          else
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%i4(buffer%is:buffer%ie), &
              start=dataset%group_start(1:ndim,i), &
              count=dataset%group_count(1:ndim,i))
          end if
          if (fatal_opt) then
            call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                              trim(dataset%file_path) // """!", __FILE__, __LINE__)
          end if
        end do
      end if
      call buffer%clear()
    end if
    ! if (dataset%proc_id == 0) then
    !   time2 = MPI_WTIME()
    !   print *, "   - write cost ", time2 - time1
    ! end if

  end subroutine fiona_output_2d_i4

  subroutine fiona_output_2d_r4(dataset_name, var_name, array, start, count, fatal, only_root)

    integer, parameter :: ndim = 2

    character(*), intent(in) :: dataset_name
    character(*), intent(in) :: var_name
    real(4), intent(in) :: array(:,:)
    integer, intent(in), optional :: start(ndim)
    integer, intent(in), optional :: count(ndim)
    logical, intent(in), optional :: fatal
    logical, intent(in), optional :: only_root

    type(dataset_type), pointer :: dataset
    type(var_type), pointer :: var
    type(buffer_type) buffer
    logical fatal_opt
    logical only_root_opt
    integer i, ierr
    real(8) time1, time2

    fatal_opt = .false.; if (present(fatal)) fatal_opt = fatal
    only_root_opt = .false.; if (present(only_root)) only_root_opt = only_root

    dataset => get_dataset(dataset_name)
    ! if (dataset%proc_id == 0) then
    !   time1 = MPI_WTIME()
    !   print *, " - output " // trim(var_name) // ":"
    ! end if
    var => dataset%get_var(var_name)
    if (only_root_opt) then
      ierr =  NF90_PUT_VAR(dataset%id, var%id, array)
      if (fatal_opt) then
        call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                          trim(dataset%file_path) // """!", __FILE__, __LINE__)
      end if
    else
      call gather_start_count(dataset, ndim, shape(array), start, count)
      if (dataset%is_group_root) then
        call buffer%init(buffer_size(dataset, ndim), array(1,1))
#ifdef HAS_MPI
        if (dataset%is_parallel) then
          if (dataset%time_dim%size == NF90_UNLIMITED) then
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_COLLECTIVE)
          else
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_INDEPENDENT)
          end if
          call handle_error(ierr, "Failed to set parallel access for variable " // trim(var%name) // "!", __FILE__, __LINE__)
        end if
#endif
      end if
      do i = 1, dataset%group_nproc
        if (dataset%is_group_root .or. dataset%group_proc_id == i - 1) then
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          if (dataset%is_group_root) then
            call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          end if
          if (dataset%is_group_root) then
            if (i == 1) then
              buffer%r4(buffer%is:buffer%ie) = reshape(array, [size(array)])
            else
#ifdef HAS_MPI
              call MPI_IRECV(buffer%r4(buffer%is:buffer%ie), buffer%ie-buffer%is+1, MPI_FLOAT, i-1, 88, &
                             dataset%group_mpi_comm, dataset%group_recv_req(i), ierr)
#endif
            end if
          else
#ifdef HAS_MPI
            call MPI_SEND(array, size(array), MPI_FLOAT, 0, 88, dataset%group_mpi_comm, ierr)
#endif
          end if
        end if
      end do
      ! Write data into file.
      call buffer%shift_index_back()
      if (dataset%is_group_root) then
        do i = 1, dataset%group_nproc
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          if (i > 1) call dataset%wait_recv(i)
          if (var%has_unlimited_dim) then
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%r4(buffer%is:buffer%ie), &
              start=[dataset%group_start(1:ndim,i),dataset%time_step], &
              count=[dataset%group_count(1:ndim,i),1])
          else
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%r4(buffer%is:buffer%ie), &
              start=dataset%group_start(1:ndim,i), &
              count=dataset%group_count(1:ndim,i))
          end if
          if (fatal_opt) then
            call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                              trim(dataset%file_path) // """!", __FILE__, __LINE__)
          end if
        end do
      end if
      call buffer%clear()
    end if
    ! if (dataset%proc_id == 0) then
    !   time2 = MPI_WTIME()
    !   print *, "   - write cost ", time2 - time1
    ! end if

  end subroutine fiona_output_2d_r4

  subroutine fiona_output_2d_r8(dataset_name, var_name, array, start, count, fatal, only_root)

    integer, parameter :: ndim = 2

    character(*), intent(in) :: dataset_name
    character(*), intent(in) :: var_name
    real(8), intent(in) :: array(:,:)
    integer, intent(in), optional :: start(ndim)
    integer, intent(in), optional :: count(ndim)
    logical, intent(in), optional :: fatal
    logical, intent(in), optional :: only_root

    type(dataset_type), pointer :: dataset
    type(var_type), pointer :: var
    type(buffer_type) buffer
    logical fatal_opt
    logical only_root_opt
    integer i, ierr
    real(8) time1, time2

    fatal_opt = .false.; if (present(fatal)) fatal_opt = fatal
    only_root_opt = .false.; if (present(only_root)) only_root_opt = only_root

    dataset => get_dataset(dataset_name)
    ! if (dataset%proc_id == 0) then
    !   time1 = MPI_WTIME()
    !   print *, " - output " // trim(var_name) // ":"
    ! end if
    var => dataset%get_var(var_name)
    if (only_root_opt) then
      ierr =  NF90_PUT_VAR(dataset%id, var%id, array)
      if (fatal_opt) then
        call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                          trim(dataset%file_path) // """!", __FILE__, __LINE__)
      end if
    else
      call gather_start_count(dataset, ndim, shape(array), start, count)
      if (dataset%is_group_root) then
        call buffer%init(buffer_size(dataset, ndim), array(1,1))
#ifdef HAS_MPI
        if (dataset%is_parallel) then
          if (dataset%time_dim%size == NF90_UNLIMITED) then
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_COLLECTIVE)
          else
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_INDEPENDENT)
          end if
          call handle_error(ierr, "Failed to set parallel access for variable " // trim(var%name) // "!", __FILE__, __LINE__)
        end if
#endif
      end if
      do i = 1, dataset%group_nproc
        if (dataset%is_group_root .or. dataset%group_proc_id == i - 1) then
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          if (dataset%is_group_root) then
            call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          end if
          if (dataset%is_group_root) then
            if (i == 1) then
              buffer%r8(buffer%is:buffer%ie) = reshape(array, [size(array)])
            else
#ifdef HAS_MPI
              call MPI_IRECV(buffer%r8(buffer%is:buffer%ie), buffer%ie-buffer%is+1, MPI_DOUBLE, i-1, 88, &
                             dataset%group_mpi_comm, dataset%group_recv_req(i), ierr)
#endif
            end if
          else
#ifdef HAS_MPI
            call MPI_SEND(array, size(array), MPI_DOUBLE, 0, 88, dataset%group_mpi_comm, ierr)
#endif
          end if
        end if
      end do
      ! Write data into file.
      call buffer%shift_index_back()
      if (dataset%is_group_root) then
        do i = 1, dataset%group_nproc
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          if (i > 1) call dataset%wait_recv(i)
          if (var%has_unlimited_dim) then
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%r8(buffer%is:buffer%ie), &
              start=[dataset%group_start(1:ndim,i),dataset%time_step], &
              count=[dataset%group_count(1:ndim,i),1])
          else
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%r8(buffer%is:buffer%ie), &
              start=dataset%group_start(1:ndim,i), &
              count=dataset%group_count(1:ndim,i))
          end if
          if (fatal_opt) then
            call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                              trim(dataset%file_path) // """!", __FILE__, __LINE__)
          end if
        end do
      end if
      call buffer%clear()
    end if
    ! if (dataset%proc_id == 0) then
    !   time2 = MPI_WTIME()
    !   print *, "   - write cost ", time2 - time1
    ! end if

  end subroutine fiona_output_2d_r8

  subroutine fiona_output_3d_i2(dataset_name, var_name, array, start, count, fatal, only_root)

    integer, parameter :: ndim = 3

    character(*), intent(in) :: dataset_name
    character(*), intent(in) :: var_name
    integer(2), intent(in) :: array(:,:,:)
    integer, intent(in), optional :: start(ndim)
    integer, intent(in), optional :: count(ndim)
    logical, intent(in), optional :: fatal
    logical, intent(in), optional :: only_root

    type(dataset_type), pointer :: dataset
    type(var_type), pointer :: var
    type(buffer_type) buffer
    logical fatal_opt
    logical only_root_opt
    integer i, ierr
    real(8) time1, time2

    fatal_opt = .false.; if (present(fatal)) fatal_opt = fatal
    only_root_opt = .false.; if (present(only_root)) only_root_opt = only_root

    dataset => get_dataset(dataset_name)
    ! if (dataset%proc_id == 0) then
    !   time1 = MPI_WTIME()
    !   print *, " - output " // trim(var_name) // ":"
    ! end if
    var => dataset%get_var(var_name)
    if (only_root_opt) then
      ierr =  NF90_PUT_VAR(dataset%id, var%id, array)
      if (fatal_opt) then
        call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                          trim(dataset%file_path) // """!", __FILE__, __LINE__)
      end if
    else
      call gather_start_count(dataset, ndim, shape(array), start, count)
      if (dataset%is_group_root) then
        call buffer%init(buffer_size(dataset, ndim), array(1,1,1))
#ifdef HAS_MPI
        if (dataset%is_parallel) then
          if (dataset%time_dim%size == NF90_UNLIMITED) then
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_COLLECTIVE)
          else
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_INDEPENDENT)
          end if
          call handle_error(ierr, "Failed to set parallel access for variable " // trim(var%name) // "!", __FILE__, __LINE__)
        end if
#endif
      end if
      do i = 1, dataset%group_nproc
        if (dataset%is_group_root .or. dataset%group_proc_id == i - 1) then
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          if (dataset%is_group_root) then
            call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          end if
          if (dataset%is_group_root) then
            if (i == 1) then
              buffer%i2(buffer%is:buffer%ie) = reshape(array, [size(array)])
            else
#ifdef HAS_MPI
              call MPI_IRECV(buffer%i2(buffer%is:buffer%ie), buffer%ie-buffer%is+1, MPI_SHORT, i-1, 88, &
                             dataset%group_mpi_comm, dataset%group_recv_req(i), ierr)
#endif
            end if
          else
#ifdef HAS_MPI
            call MPI_SEND(array, size(array), MPI_SHORT, 0, 88, dataset%group_mpi_comm, ierr)
#endif
          end if
        end if
      end do
      ! Write data into file.
      call buffer%shift_index_back()
      if (dataset%is_group_root) then
        do i = 1, dataset%group_nproc
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          if (i > 1) call dataset%wait_recv(i)
          if (var%has_unlimited_dim) then
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%i2(buffer%is:buffer%ie), &
              start=[dataset%group_start(1:ndim,i),dataset%time_step], &
              count=[dataset%group_count(1:ndim,i),1])
          else
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%i2(buffer%is:buffer%ie), &
              start=dataset%group_start(1:ndim,i), &
              count=dataset%group_count(1:ndim,i))
          end if
          if (fatal_opt) then
            call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                              trim(dataset%file_path) // """!", __FILE__, __LINE__)
          end if
        end do
      end if
      call buffer%clear()
    end if
    ! if (dataset%proc_id == 0) then
    !   time2 = MPI_WTIME()
    !   print *, "   - write cost ", time2 - time1
    ! end if

  end subroutine fiona_output_3d_i2

  subroutine fiona_output_3d_i4(dataset_name, var_name, array, start, count, fatal, only_root)

    integer, parameter :: ndim = 3

    character(*), intent(in) :: dataset_name
    character(*), intent(in) :: var_name
    integer(4), intent(in) :: array(:,:,:)
    integer, intent(in), optional :: start(ndim)
    integer, intent(in), optional :: count(ndim)
    logical, intent(in), optional :: fatal
    logical, intent(in), optional :: only_root

    type(dataset_type), pointer :: dataset
    type(var_type), pointer :: var
    type(buffer_type) buffer
    logical fatal_opt
    logical only_root_opt
    integer i, ierr
    real(8) time1, time2

    fatal_opt = .false.; if (present(fatal)) fatal_opt = fatal
    only_root_opt = .false.; if (present(only_root)) only_root_opt = only_root

    dataset => get_dataset(dataset_name)
    ! if (dataset%proc_id == 0) then
    !   time1 = MPI_WTIME()
    !   print *, " - output " // trim(var_name) // ":"
    ! end if
    var => dataset%get_var(var_name)
    if (only_root_opt) then
      ierr =  NF90_PUT_VAR(dataset%id, var%id, array)
      if (fatal_opt) then
        call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                          trim(dataset%file_path) // """!", __FILE__, __LINE__)
      end if
    else
      call gather_start_count(dataset, ndim, shape(array), start, count)
      if (dataset%is_group_root) then
        call buffer%init(buffer_size(dataset, ndim), array(1,1,1))
#ifdef HAS_MPI
        if (dataset%is_parallel) then
          if (dataset%time_dim%size == NF90_UNLIMITED) then
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_COLLECTIVE)
          else
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_INDEPENDENT)
          end if
          call handle_error(ierr, "Failed to set parallel access for variable " // trim(var%name) // "!", __FILE__, __LINE__)
        end if
#endif
      end if
      do i = 1, dataset%group_nproc
        if (dataset%is_group_root .or. dataset%group_proc_id == i - 1) then
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          if (dataset%is_group_root) then
            call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          end if
          if (dataset%is_group_root) then
            if (i == 1) then
              buffer%i4(buffer%is:buffer%ie) = reshape(array, [size(array)])
            else
#ifdef HAS_MPI
              call MPI_IRECV(buffer%i4(buffer%is:buffer%ie), buffer%ie-buffer%is+1, MPI_INT, i-1, 88, &
                             dataset%group_mpi_comm, dataset%group_recv_req(i), ierr)
#endif
            end if
          else
#ifdef HAS_MPI
            call MPI_SEND(array, size(array), MPI_INT, 0, 88, dataset%group_mpi_comm, ierr)
#endif
          end if
        end if
      end do
      ! Write data into file.
      call buffer%shift_index_back()
      if (dataset%is_group_root) then
        do i = 1, dataset%group_nproc
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          if (i > 1) call dataset%wait_recv(i)
          if (var%has_unlimited_dim) then
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%i4(buffer%is:buffer%ie), &
              start=[dataset%group_start(1:ndim,i),dataset%time_step], &
              count=[dataset%group_count(1:ndim,i),1])
          else
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%i4(buffer%is:buffer%ie), &
              start=dataset%group_start(1:ndim,i), &
              count=dataset%group_count(1:ndim,i))
          end if
          if (fatal_opt) then
            call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                              trim(dataset%file_path) // """!", __FILE__, __LINE__)
          end if
        end do
      end if
      call buffer%clear()
    end if
    ! if (dataset%proc_id == 0) then
    !   time2 = MPI_WTIME()
    !   print *, "   - write cost ", time2 - time1
    ! end if

  end subroutine fiona_output_3d_i4

  subroutine fiona_output_3d_r4(dataset_name, var_name, array, start, count, fatal, only_root)

    integer, parameter :: ndim = 3

    character(*), intent(in) :: dataset_name
    character(*), intent(in) :: var_name
    real(4), intent(in) :: array(:,:,:)
    integer, intent(in), optional :: start(ndim)
    integer, intent(in), optional :: count(ndim)
    logical, intent(in), optional :: fatal
    logical, intent(in), optional :: only_root

    type(dataset_type), pointer :: dataset
    type(var_type), pointer :: var
    type(buffer_type) buffer
    logical fatal_opt
    logical only_root_opt
    integer i, ierr
    real(8) time1, time2

    fatal_opt = .false.; if (present(fatal)) fatal_opt = fatal
    only_root_opt = .false.; if (present(only_root)) only_root_opt = only_root

    dataset => get_dataset(dataset_name)
    ! if (dataset%proc_id == 0) then
    !   time1 = MPI_WTIME()
    !   print *, " - output " // trim(var_name) // ":"
    ! end if
    var => dataset%get_var(var_name)
    if (only_root_opt) then
      ierr =  NF90_PUT_VAR(dataset%id, var%id, array)
      if (fatal_opt) then
        call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                          trim(dataset%file_path) // """!", __FILE__, __LINE__)
      end if
    else
      call gather_start_count(dataset, ndim, shape(array), start, count)
      if (dataset%is_group_root) then
        call buffer%init(buffer_size(dataset, ndim), array(1,1,1))
#ifdef HAS_MPI
        if (dataset%is_parallel) then
          if (dataset%time_dim%size == NF90_UNLIMITED) then
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_COLLECTIVE)
          else
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_INDEPENDENT)
          end if
          call handle_error(ierr, "Failed to set parallel access for variable " // trim(var%name) // "!", __FILE__, __LINE__)
        end if
#endif
      end if
      do i = 1, dataset%group_nproc
        if (dataset%is_group_root .or. dataset%group_proc_id == i - 1) then
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          if (dataset%is_group_root) then
            call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          end if
          if (dataset%is_group_root) then
            if (i == 1) then
              buffer%r4(buffer%is:buffer%ie) = reshape(array, [size(array)])
            else
#ifdef HAS_MPI
              call MPI_IRECV(buffer%r4(buffer%is:buffer%ie), buffer%ie-buffer%is+1, MPI_FLOAT, i-1, 88, &
                             dataset%group_mpi_comm, dataset%group_recv_req(i), ierr)
#endif
            end if
          else
#ifdef HAS_MPI
            call MPI_SEND(array, size(array), MPI_FLOAT, 0, 88, dataset%group_mpi_comm, ierr)
#endif
          end if
        end if
      end do
      ! Write data into file.
      call buffer%shift_index_back()
      if (dataset%is_group_root) then
        do i = 1, dataset%group_nproc
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          if (i > 1) call dataset%wait_recv(i)
          if (var%has_unlimited_dim) then
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%r4(buffer%is:buffer%ie), &
              start=[dataset%group_start(1:ndim,i),dataset%time_step], &
              count=[dataset%group_count(1:ndim,i),1])
          else
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%r4(buffer%is:buffer%ie), &
              start=dataset%group_start(1:ndim,i), &
              count=dataset%group_count(1:ndim,i))
          end if
          if (fatal_opt) then
            call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                              trim(dataset%file_path) // """!", __FILE__, __LINE__)
          end if
        end do
      end if
      call buffer%clear()
    end if
    ! if (dataset%proc_id == 0) then
    !   time2 = MPI_WTIME()
    !   print *, "   - write cost ", time2 - time1
    ! end if

  end subroutine fiona_output_3d_r4

  subroutine fiona_output_3d_r8(dataset_name, var_name, array, start, count, fatal, only_root)

    integer, parameter :: ndim = 3

    character(*), intent(in) :: dataset_name
    character(*), intent(in) :: var_name
    real(8), intent(in) :: array(:,:,:)
    integer, intent(in), optional :: start(ndim)
    integer, intent(in), optional :: count(ndim)
    logical, intent(in), optional :: fatal
    logical, intent(in), optional :: only_root

    type(dataset_type), pointer :: dataset
    type(var_type), pointer :: var
    type(buffer_type) buffer
    logical fatal_opt
    logical only_root_opt
    integer i, ierr
    real(8) time1, time2

    fatal_opt = .false.; if (present(fatal)) fatal_opt = fatal
    only_root_opt = .false.; if (present(only_root)) only_root_opt = only_root

    dataset => get_dataset(dataset_name)
    ! if (dataset%proc_id == 0) then
    !   time1 = MPI_WTIME()
    !   print *, " - output " // trim(var_name) // ":"
    ! end if
    var => dataset%get_var(var_name)
    if (only_root_opt) then
      ierr =  NF90_PUT_VAR(dataset%id, var%id, array)
      if (fatal_opt) then
        call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                          trim(dataset%file_path) // """!", __FILE__, __LINE__)
      end if
    else
      call gather_start_count(dataset, ndim, shape(array), start, count)
      if (dataset%is_group_root) then
        call buffer%init(buffer_size(dataset, ndim), array(1,1,1))
#ifdef HAS_MPI
        if (dataset%is_parallel) then
          if (dataset%time_dim%size == NF90_UNLIMITED) then
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_COLLECTIVE)
          else
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_INDEPENDENT)
          end if
          call handle_error(ierr, "Failed to set parallel access for variable " // trim(var%name) // "!", __FILE__, __LINE__)
        end if
#endif
      end if
      do i = 1, dataset%group_nproc
        if (dataset%is_group_root .or. dataset%group_proc_id == i - 1) then
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          if (dataset%is_group_root) then
            call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          end if
          if (dataset%is_group_root) then
            if (i == 1) then
              buffer%r8(buffer%is:buffer%ie) = reshape(array, [size(array)])
            else
#ifdef HAS_MPI
              call MPI_IRECV(buffer%r8(buffer%is:buffer%ie), buffer%ie-buffer%is+1, MPI_DOUBLE, i-1, 88, &
                             dataset%group_mpi_comm, dataset%group_recv_req(i), ierr)
#endif
            end if
          else
#ifdef HAS_MPI
            call MPI_SEND(array, size(array), MPI_DOUBLE, 0, 88, dataset%group_mpi_comm, ierr)
#endif
          end if
        end if
      end do
      ! Write data into file.
      call buffer%shift_index_back()
      if (dataset%is_group_root) then
        do i = 1, dataset%group_nproc
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          if (i > 1) call dataset%wait_recv(i)
          if (var%has_unlimited_dim) then
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%r8(buffer%is:buffer%ie), &
              start=[dataset%group_start(1:ndim,i),dataset%time_step], &
              count=[dataset%group_count(1:ndim,i),1])
          else
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%r8(buffer%is:buffer%ie), &
              start=dataset%group_start(1:ndim,i), &
              count=dataset%group_count(1:ndim,i))
          end if
          if (fatal_opt) then
            call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                              trim(dataset%file_path) // """!", __FILE__, __LINE__)
          end if
        end do
      end if
      call buffer%clear()
    end if
    ! if (dataset%proc_id == 0) then
    !   time2 = MPI_WTIME()
    !   print *, "   - write cost ", time2 - time1
    ! end if

  end subroutine fiona_output_3d_r8

  subroutine fiona_output_4d_i2(dataset_name, var_name, array, start, count, fatal, only_root)

    integer, parameter :: ndim = 4

    character(*), intent(in) :: dataset_name
    character(*), intent(in) :: var_name
    integer(2), intent(in) :: array(:,:,:,:)
    integer, intent(in), optional :: start(ndim)
    integer, intent(in), optional :: count(ndim)
    logical, intent(in), optional :: fatal
    logical, intent(in), optional :: only_root

    type(dataset_type), pointer :: dataset
    type(var_type), pointer :: var
    type(buffer_type) buffer
    logical fatal_opt
    logical only_root_opt
    integer i, ierr
    real(8) time1, time2

    fatal_opt = .false.; if (present(fatal)) fatal_opt = fatal
    only_root_opt = .false.; if (present(only_root)) only_root_opt = only_root

    dataset => get_dataset(dataset_name)
    ! if (dataset%proc_id == 0) then
    !   time1 = MPI_WTIME()
    !   print *, " - output " // trim(var_name) // ":"
    ! end if
    var => dataset%get_var(var_name)
    if (only_root_opt) then
      ierr =  NF90_PUT_VAR(dataset%id, var%id, array)
      if (fatal_opt) then
        call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                          trim(dataset%file_path) // """!", __FILE__, __LINE__)
      end if
    else
      call gather_start_count(dataset, ndim, shape(array), start, count)
      if (dataset%is_group_root) then
        call buffer%init(buffer_size(dataset, ndim), array(1,1,1,1))
#ifdef HAS_MPI
        if (dataset%is_parallel) then
          if (dataset%time_dim%size == NF90_UNLIMITED) then
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_COLLECTIVE)
          else
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_INDEPENDENT)
          end if
          call handle_error(ierr, "Failed to set parallel access for variable " // trim(var%name) // "!", __FILE__, __LINE__)
        end if
#endif
      end if
      do i = 1, dataset%group_nproc
        if (dataset%is_group_root .or. dataset%group_proc_id == i - 1) then
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          if (dataset%is_group_root) then
            call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          end if
          if (dataset%is_group_root) then
            if (i == 1) then
              buffer%i2(buffer%is:buffer%ie) = reshape(array, [size(array)])
            else
#ifdef HAS_MPI
              call MPI_IRECV(buffer%i2(buffer%is:buffer%ie), buffer%ie-buffer%is+1, MPI_SHORT, i-1, 88, &
                             dataset%group_mpi_comm, dataset%group_recv_req(i), ierr)
#endif
            end if
          else
#ifdef HAS_MPI
            call MPI_SEND(array, size(array), MPI_SHORT, 0, 88, dataset%group_mpi_comm, ierr)
#endif
          end if
        end if
      end do
      ! Write data into file.
      call buffer%shift_index_back()
      if (dataset%is_group_root) then
        do i = 1, dataset%group_nproc
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          if (i > 1) call dataset%wait_recv(i)
          if (var%has_unlimited_dim) then
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%i2(buffer%is:buffer%ie), &
              start=[dataset%group_start(1:ndim,i),dataset%time_step], &
              count=[dataset%group_count(1:ndim,i),1])
          else
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%i2(buffer%is:buffer%ie), &
              start=dataset%group_start(1:ndim,i), &
              count=dataset%group_count(1:ndim,i))
          end if
          if (fatal_opt) then
            call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                              trim(dataset%file_path) // """!", __FILE__, __LINE__)
          end if
        end do
      end if
      call buffer%clear()
    end if
    ! if (dataset%proc_id == 0) then
    !   time2 = MPI_WTIME()
    !   print *, "   - write cost ", time2 - time1
    ! end if

  end subroutine fiona_output_4d_i2

  subroutine fiona_output_4d_i4(dataset_name, var_name, array, start, count, fatal, only_root)

    integer, parameter :: ndim = 4

    character(*), intent(in) :: dataset_name
    character(*), intent(in) :: var_name
    integer(4), intent(in) :: array(:,:,:,:)
    integer, intent(in), optional :: start(ndim)
    integer, intent(in), optional :: count(ndim)
    logical, intent(in), optional :: fatal
    logical, intent(in), optional :: only_root

    type(dataset_type), pointer :: dataset
    type(var_type), pointer :: var
    type(buffer_type) buffer
    logical fatal_opt
    logical only_root_opt
    integer i, ierr
    real(8) time1, time2

    fatal_opt = .false.; if (present(fatal)) fatal_opt = fatal
    only_root_opt = .false.; if (present(only_root)) only_root_opt = only_root

    dataset => get_dataset(dataset_name)
    ! if (dataset%proc_id == 0) then
    !   time1 = MPI_WTIME()
    !   print *, " - output " // trim(var_name) // ":"
    ! end if
    var => dataset%get_var(var_name)
    if (only_root_opt) then
      ierr =  NF90_PUT_VAR(dataset%id, var%id, array)
      if (fatal_opt) then
        call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                          trim(dataset%file_path) // """!", __FILE__, __LINE__)
      end if
    else
      call gather_start_count(dataset, ndim, shape(array), start, count)
      if (dataset%is_group_root) then
        call buffer%init(buffer_size(dataset, ndim), array(1,1,1,1))
#ifdef HAS_MPI
        if (dataset%is_parallel) then
          if (dataset%time_dim%size == NF90_UNLIMITED) then
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_COLLECTIVE)
          else
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_INDEPENDENT)
          end if
          call handle_error(ierr, "Failed to set parallel access for variable " // trim(var%name) // "!", __FILE__, __LINE__)
        end if
#endif
      end if
      do i = 1, dataset%group_nproc
        if (dataset%is_group_root .or. dataset%group_proc_id == i - 1) then
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          if (dataset%is_group_root) then
            call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          end if
          if (dataset%is_group_root) then
            if (i == 1) then
              buffer%i4(buffer%is:buffer%ie) = reshape(array, [size(array)])
            else
#ifdef HAS_MPI
              call MPI_IRECV(buffer%i4(buffer%is:buffer%ie), buffer%ie-buffer%is+1, MPI_INT, i-1, 88, &
                             dataset%group_mpi_comm, dataset%group_recv_req(i), ierr)
#endif
            end if
          else
#ifdef HAS_MPI
            call MPI_SEND(array, size(array), MPI_INT, 0, 88, dataset%group_mpi_comm, ierr)
#endif
          end if
        end if
      end do
      ! Write data into file.
      call buffer%shift_index_back()
      if (dataset%is_group_root) then
        do i = 1, dataset%group_nproc
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          if (i > 1) call dataset%wait_recv(i)
          if (var%has_unlimited_dim) then
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%i4(buffer%is:buffer%ie), &
              start=[dataset%group_start(1:ndim,i),dataset%time_step], &
              count=[dataset%group_count(1:ndim,i),1])
          else
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%i4(buffer%is:buffer%ie), &
              start=dataset%group_start(1:ndim,i), &
              count=dataset%group_count(1:ndim,i))
          end if
          if (fatal_opt) then
            call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                              trim(dataset%file_path) // """!", __FILE__, __LINE__)
          end if
        end do
      end if
      call buffer%clear()
    end if
    ! if (dataset%proc_id == 0) then
    !   time2 = MPI_WTIME()
    !   print *, "   - write cost ", time2 - time1
    ! end if

  end subroutine fiona_output_4d_i4

  subroutine fiona_output_4d_r4(dataset_name, var_name, array, start, count, fatal, only_root)

    integer, parameter :: ndim = 4

    character(*), intent(in) :: dataset_name
    character(*), intent(in) :: var_name
    real(4), intent(in) :: array(:,:,:,:)
    integer, intent(in), optional :: start(ndim)
    integer, intent(in), optional :: count(ndim)
    logical, intent(in), optional :: fatal
    logical, intent(in), optional :: only_root

    type(dataset_type), pointer :: dataset
    type(var_type), pointer :: var
    type(buffer_type) buffer
    logical fatal_opt
    logical only_root_opt
    integer i, ierr
    real(8) time1, time2

    fatal_opt = .false.; if (present(fatal)) fatal_opt = fatal
    only_root_opt = .false.; if (present(only_root)) only_root_opt = only_root

    dataset => get_dataset(dataset_name)
    ! if (dataset%proc_id == 0) then
    !   time1 = MPI_WTIME()
    !   print *, " - output " // trim(var_name) // ":"
    ! end if
    var => dataset%get_var(var_name)
    if (only_root_opt) then
      ierr =  NF90_PUT_VAR(dataset%id, var%id, array)
      if (fatal_opt) then
        call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                          trim(dataset%file_path) // """!", __FILE__, __LINE__)
      end if
    else
      call gather_start_count(dataset, ndim, shape(array), start, count)
      if (dataset%is_group_root) then
        call buffer%init(buffer_size(dataset, ndim), array(1,1,1,1))
#ifdef HAS_MPI
        if (dataset%is_parallel) then
          if (dataset%time_dim%size == NF90_UNLIMITED) then
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_COLLECTIVE)
          else
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_INDEPENDENT)
          end if
          call handle_error(ierr, "Failed to set parallel access for variable " // trim(var%name) // "!", __FILE__, __LINE__)
        end if
#endif
      end if
      do i = 1, dataset%group_nproc
        if (dataset%is_group_root .or. dataset%group_proc_id == i - 1) then
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          if (dataset%is_group_root) then
            call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          end if
          if (dataset%is_group_root) then
            if (i == 1) then
              buffer%r4(buffer%is:buffer%ie) = reshape(array, [size(array)])
            else
#ifdef HAS_MPI
              call MPI_IRECV(buffer%r4(buffer%is:buffer%ie), buffer%ie-buffer%is+1, MPI_FLOAT, i-1, 88, &
                             dataset%group_mpi_comm, dataset%group_recv_req(i), ierr)
#endif
            end if
          else
#ifdef HAS_MPI
            call MPI_SEND(array, size(array), MPI_FLOAT, 0, 88, dataset%group_mpi_comm, ierr)
#endif
          end if
        end if
      end do
      ! Write data into file.
      call buffer%shift_index_back()
      if (dataset%is_group_root) then
        do i = 1, dataset%group_nproc
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          if (i > 1) call dataset%wait_recv(i)
          if (var%has_unlimited_dim) then
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%r4(buffer%is:buffer%ie), &
              start=[dataset%group_start(1:ndim,i),dataset%time_step], &
              count=[dataset%group_count(1:ndim,i),1])
          else
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%r4(buffer%is:buffer%ie), &
              start=dataset%group_start(1:ndim,i), &
              count=dataset%group_count(1:ndim,i))
          end if
          if (fatal_opt) then
            call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                              trim(dataset%file_path) // """!", __FILE__, __LINE__)
          end if
        end do
      end if
      call buffer%clear()
    end if
    ! if (dataset%proc_id == 0) then
    !   time2 = MPI_WTIME()
    !   print *, "   - write cost ", time2 - time1
    ! end if

  end subroutine fiona_output_4d_r4

  subroutine fiona_output_4d_r8(dataset_name, var_name, array, start, count, fatal, only_root)

    integer, parameter :: ndim = 4

    character(*), intent(in) :: dataset_name
    character(*), intent(in) :: var_name
    real(8), intent(in) :: array(:,:,:,:)
    integer, intent(in), optional :: start(ndim)
    integer, intent(in), optional :: count(ndim)
    logical, intent(in), optional :: fatal
    logical, intent(in), optional :: only_root

    type(dataset_type), pointer :: dataset
    type(var_type), pointer :: var
    type(buffer_type) buffer
    logical fatal_opt
    logical only_root_opt
    integer i, ierr
    real(8) time1, time2

    fatal_opt = .false.; if (present(fatal)) fatal_opt = fatal
    only_root_opt = .false.; if (present(only_root)) only_root_opt = only_root

    dataset => get_dataset(dataset_name)
    ! if (dataset%proc_id == 0) then
    !   time1 = MPI_WTIME()
    !   print *, " - output " // trim(var_name) // ":"
    ! end if
    var => dataset%get_var(var_name)
    if (only_root_opt) then
      ierr =  NF90_PUT_VAR(dataset%id, var%id, array)
      if (fatal_opt) then
        call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                          trim(dataset%file_path) // """!", __FILE__, __LINE__)
      end if
    else
      call gather_start_count(dataset, ndim, shape(array), start, count)
      if (dataset%is_group_root) then
        call buffer%init(buffer_size(dataset, ndim), array(1,1,1,1))
#ifdef HAS_MPI
        if (dataset%is_parallel) then
          if (dataset%time_dim%size == NF90_UNLIMITED) then
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_COLLECTIVE)
          else
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_INDEPENDENT)
          end if
          call handle_error(ierr, "Failed to set parallel access for variable " // trim(var%name) // "!", __FILE__, __LINE__)
        end if
#endif
      end if
      do i = 1, dataset%group_nproc
        if (dataset%is_group_root .or. dataset%group_proc_id == i - 1) then
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          if (dataset%is_group_root) then
            call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          end if
          if (dataset%is_group_root) then
            if (i == 1) then
              buffer%r8(buffer%is:buffer%ie) = reshape(array, [size(array)])
            else
#ifdef HAS_MPI
              call MPI_IRECV(buffer%r8(buffer%is:buffer%ie), buffer%ie-buffer%is+1, MPI_DOUBLE, i-1, 88, &
                             dataset%group_mpi_comm, dataset%group_recv_req(i), ierr)
#endif
            end if
          else
#ifdef HAS_MPI
            call MPI_SEND(array, size(array), MPI_DOUBLE, 0, 88, dataset%group_mpi_comm, ierr)
#endif
          end if
        end if
      end do
      ! Write data into file.
      call buffer%shift_index_back()
      if (dataset%is_group_root) then
        do i = 1, dataset%group_nproc
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          if (i > 1) call dataset%wait_recv(i)
          if (var%has_unlimited_dim) then
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%r8(buffer%is:buffer%ie), &
              start=[dataset%group_start(1:ndim,i),dataset%time_step], &
              count=[dataset%group_count(1:ndim,i),1])
          else
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%r8(buffer%is:buffer%ie), &
              start=dataset%group_start(1:ndim,i), &
              count=dataset%group_count(1:ndim,i))
          end if
          if (fatal_opt) then
            call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                              trim(dataset%file_path) // """!", __FILE__, __LINE__)
          end if
        end do
      end if
      call buffer%clear()
    end if
    ! if (dataset%proc_id == 0) then
    !   time2 = MPI_WTIME()
    !   print *, "   - write cost ", time2 - time1
    ! end if

  end subroutine fiona_output_4d_r8

  subroutine fiona_output_5d_i2(dataset_name, var_name, array, start, count, fatal, only_root)

    integer, parameter :: ndim = 5

    character(*), intent(in) :: dataset_name
    character(*), intent(in) :: var_name
    integer(2), intent(in) :: array(:,:,:,:,:)
    integer, intent(in), optional :: start(ndim)
    integer, intent(in), optional :: count(ndim)
    logical, intent(in), optional :: fatal
    logical, intent(in), optional :: only_root

    type(dataset_type), pointer :: dataset
    type(var_type), pointer :: var
    type(buffer_type) buffer
    logical fatal_opt
    logical only_root_opt
    integer i, ierr
    real(8) time1, time2

    fatal_opt = .false.; if (present(fatal)) fatal_opt = fatal
    only_root_opt = .false.; if (present(only_root)) only_root_opt = only_root

    dataset => get_dataset(dataset_name)
    ! if (dataset%proc_id == 0) then
    !   time1 = MPI_WTIME()
    !   print *, " - output " // trim(var_name) // ":"
    ! end if
    var => dataset%get_var(var_name)
    if (only_root_opt) then
      ierr =  NF90_PUT_VAR(dataset%id, var%id, array)
      if (fatal_opt) then
        call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                          trim(dataset%file_path) // """!", __FILE__, __LINE__)
      end if
    else
      call gather_start_count(dataset, ndim, shape(array), start, count)
      if (dataset%is_group_root) then
        call buffer%init(buffer_size(dataset, ndim), array(1,1,1,1,1))
#ifdef HAS_MPI
        if (dataset%is_parallel) then
          if (dataset%time_dim%size == NF90_UNLIMITED) then
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_COLLECTIVE)
          else
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_INDEPENDENT)
          end if
          call handle_error(ierr, "Failed to set parallel access for variable " // trim(var%name) // "!", __FILE__, __LINE__)
        end if
#endif
      end if
      do i = 1, dataset%group_nproc
        if (dataset%is_group_root .or. dataset%group_proc_id == i - 1) then
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          if (dataset%is_group_root) then
            call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          end if
          if (dataset%is_group_root) then
            if (i == 1) then
              buffer%i2(buffer%is:buffer%ie) = reshape(array, [size(array)])
            else
#ifdef HAS_MPI
              call MPI_IRECV(buffer%i2(buffer%is:buffer%ie), buffer%ie-buffer%is+1, MPI_SHORT, i-1, 88, &
                             dataset%group_mpi_comm, dataset%group_recv_req(i), ierr)
#endif
            end if
          else
#ifdef HAS_MPI
            call MPI_SEND(array, size(array), MPI_SHORT, 0, 88, dataset%group_mpi_comm, ierr)
#endif
          end if
        end if
      end do
      ! Write data into file.
      call buffer%shift_index_back()
      if (dataset%is_group_root) then
        do i = 1, dataset%group_nproc
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          if (i > 1) call dataset%wait_recv(i)
          if (var%has_unlimited_dim) then
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%i2(buffer%is:buffer%ie), &
              start=[dataset%group_start(1:ndim,i),dataset%time_step], &
              count=[dataset%group_count(1:ndim,i),1])
          else
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%i2(buffer%is:buffer%ie), &
              start=dataset%group_start(1:ndim,i), &
              count=dataset%group_count(1:ndim,i))
          end if
          if (fatal_opt) then
            call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                              trim(dataset%file_path) // """!", __FILE__, __LINE__)
          end if
        end do
      end if
      call buffer%clear()
    end if
    ! if (dataset%proc_id == 0) then
    !   time2 = MPI_WTIME()
    !   print *, "   - write cost ", time2 - time1
    ! end if

  end subroutine fiona_output_5d_i2

  subroutine fiona_output_5d_i4(dataset_name, var_name, array, start, count, fatal, only_root)

    integer, parameter :: ndim = 5

    character(*), intent(in) :: dataset_name
    character(*), intent(in) :: var_name
    integer(4), intent(in) :: array(:,:,:,:,:)
    integer, intent(in), optional :: start(ndim)
    integer, intent(in), optional :: count(ndim)
    logical, intent(in), optional :: fatal
    logical, intent(in), optional :: only_root

    type(dataset_type), pointer :: dataset
    type(var_type), pointer :: var
    type(buffer_type) buffer
    logical fatal_opt
    logical only_root_opt
    integer i, ierr
    real(8) time1, time2

    fatal_opt = .false.; if (present(fatal)) fatal_opt = fatal
    only_root_opt = .false.; if (present(only_root)) only_root_opt = only_root

    dataset => get_dataset(dataset_name)
    ! if (dataset%proc_id == 0) then
    !   time1 = MPI_WTIME()
    !   print *, " - output " // trim(var_name) // ":"
    ! end if
    var => dataset%get_var(var_name)
    if (only_root_opt) then
      ierr =  NF90_PUT_VAR(dataset%id, var%id, array)
      if (fatal_opt) then
        call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                          trim(dataset%file_path) // """!", __FILE__, __LINE__)
      end if
    else
      call gather_start_count(dataset, ndim, shape(array), start, count)
      if (dataset%is_group_root) then
        call buffer%init(buffer_size(dataset, ndim), array(1,1,1,1,1))
#ifdef HAS_MPI
        if (dataset%is_parallel) then
          if (dataset%time_dim%size == NF90_UNLIMITED) then
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_COLLECTIVE)
          else
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_INDEPENDENT)
          end if
          call handle_error(ierr, "Failed to set parallel access for variable " // trim(var%name) // "!", __FILE__, __LINE__)
        end if
#endif
      end if
      do i = 1, dataset%group_nproc
        if (dataset%is_group_root .or. dataset%group_proc_id == i - 1) then
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          if (dataset%is_group_root) then
            call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          end if
          if (dataset%is_group_root) then
            if (i == 1) then
              buffer%i4(buffer%is:buffer%ie) = reshape(array, [size(array)])
            else
#ifdef HAS_MPI
              call MPI_IRECV(buffer%i4(buffer%is:buffer%ie), buffer%ie-buffer%is+1, MPI_INT, i-1, 88, &
                             dataset%group_mpi_comm, dataset%group_recv_req(i), ierr)
#endif
            end if
          else
#ifdef HAS_MPI
            call MPI_SEND(array, size(array), MPI_INT, 0, 88, dataset%group_mpi_comm, ierr)
#endif
          end if
        end if
      end do
      ! Write data into file.
      call buffer%shift_index_back()
      if (dataset%is_group_root) then
        do i = 1, dataset%group_nproc
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          if (i > 1) call dataset%wait_recv(i)
          if (var%has_unlimited_dim) then
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%i4(buffer%is:buffer%ie), &
              start=[dataset%group_start(1:ndim,i),dataset%time_step], &
              count=[dataset%group_count(1:ndim,i),1])
          else
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%i4(buffer%is:buffer%ie), &
              start=dataset%group_start(1:ndim,i), &
              count=dataset%group_count(1:ndim,i))
          end if
          if (fatal_opt) then
            call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                              trim(dataset%file_path) // """!", __FILE__, __LINE__)
          end if
        end do
      end if
      call buffer%clear()
    end if
    ! if (dataset%proc_id == 0) then
    !   time2 = MPI_WTIME()
    !   print *, "   - write cost ", time2 - time1
    ! end if

  end subroutine fiona_output_5d_i4

  subroutine fiona_output_5d_r4(dataset_name, var_name, array, start, count, fatal, only_root)

    integer, parameter :: ndim = 5

    character(*), intent(in) :: dataset_name
    character(*), intent(in) :: var_name
    real(4), intent(in) :: array(:,:,:,:,:)
    integer, intent(in), optional :: start(ndim)
    integer, intent(in), optional :: count(ndim)
    logical, intent(in), optional :: fatal
    logical, intent(in), optional :: only_root

    type(dataset_type), pointer :: dataset
    type(var_type), pointer :: var
    type(buffer_type) buffer
    logical fatal_opt
    logical only_root_opt
    integer i, ierr
    real(8) time1, time2

    fatal_opt = .false.; if (present(fatal)) fatal_opt = fatal
    only_root_opt = .false.; if (present(only_root)) only_root_opt = only_root

    dataset => get_dataset(dataset_name)
    ! if (dataset%proc_id == 0) then
    !   time1 = MPI_WTIME()
    !   print *, " - output " // trim(var_name) // ":"
    ! end if
    var => dataset%get_var(var_name)
    if (only_root_opt) then
      ierr =  NF90_PUT_VAR(dataset%id, var%id, array)
      if (fatal_opt) then
        call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                          trim(dataset%file_path) // """!", __FILE__, __LINE__)
      end if
    else
      call gather_start_count(dataset, ndim, shape(array), start, count)
      if (dataset%is_group_root) then
        call buffer%init(buffer_size(dataset, ndim), array(1,1,1,1,1))
#ifdef HAS_MPI
        if (dataset%is_parallel) then
          if (dataset%time_dim%size == NF90_UNLIMITED) then
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_COLLECTIVE)
          else
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_INDEPENDENT)
          end if
          call handle_error(ierr, "Failed to set parallel access for variable " // trim(var%name) // "!", __FILE__, __LINE__)
        end if
#endif
      end if
      do i = 1, dataset%group_nproc
        if (dataset%is_group_root .or. dataset%group_proc_id == i - 1) then
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          if (dataset%is_group_root) then
            call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          end if
          if (dataset%is_group_root) then
            if (i == 1) then
              buffer%r4(buffer%is:buffer%ie) = reshape(array, [size(array)])
            else
#ifdef HAS_MPI
              call MPI_IRECV(buffer%r4(buffer%is:buffer%ie), buffer%ie-buffer%is+1, MPI_FLOAT, i-1, 88, &
                             dataset%group_mpi_comm, dataset%group_recv_req(i), ierr)
#endif
            end if
          else
#ifdef HAS_MPI
            call MPI_SEND(array, size(array), MPI_FLOAT, 0, 88, dataset%group_mpi_comm, ierr)
#endif
          end if
        end if
      end do
      ! Write data into file.
      call buffer%shift_index_back()
      if (dataset%is_group_root) then
        do i = 1, dataset%group_nproc
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          if (i > 1) call dataset%wait_recv(i)
          if (var%has_unlimited_dim) then
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%r4(buffer%is:buffer%ie), &
              start=[dataset%group_start(1:ndim,i),dataset%time_step], &
              count=[dataset%group_count(1:ndim,i),1])
          else
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%r4(buffer%is:buffer%ie), &
              start=dataset%group_start(1:ndim,i), &
              count=dataset%group_count(1:ndim,i))
          end if
          if (fatal_opt) then
            call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                              trim(dataset%file_path) // """!", __FILE__, __LINE__)
          end if
        end do
      end if
      call buffer%clear()
    end if
    ! if (dataset%proc_id == 0) then
    !   time2 = MPI_WTIME()
    !   print *, "   - write cost ", time2 - time1
    ! end if

  end subroutine fiona_output_5d_r4

  subroutine fiona_output_5d_r8(dataset_name, var_name, array, start, count, fatal, only_root)

    integer, parameter :: ndim = 5

    character(*), intent(in) :: dataset_name
    character(*), intent(in) :: var_name
    real(8), intent(in) :: array(:,:,:,:,:)
    integer, intent(in), optional :: start(ndim)
    integer, intent(in), optional :: count(ndim)
    logical, intent(in), optional :: fatal
    logical, intent(in), optional :: only_root

    type(dataset_type), pointer :: dataset
    type(var_type), pointer :: var
    type(buffer_type) buffer
    logical fatal_opt
    logical only_root_opt
    integer i, ierr
    real(8) time1, time2

    fatal_opt = .false.; if (present(fatal)) fatal_opt = fatal
    only_root_opt = .false.; if (present(only_root)) only_root_opt = only_root

    dataset => get_dataset(dataset_name)
    ! if (dataset%proc_id == 0) then
    !   time1 = MPI_WTIME()
    !   print *, " - output " // trim(var_name) // ":"
    ! end if
    var => dataset%get_var(var_name)
    if (only_root_opt) then
      ierr =  NF90_PUT_VAR(dataset%id, var%id, array)
      if (fatal_opt) then
        call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                          trim(dataset%file_path) // """!", __FILE__, __LINE__)
      end if
    else
      call gather_start_count(dataset, ndim, shape(array), start, count)
      if (dataset%is_group_root) then
        call buffer%init(buffer_size(dataset, ndim), array(1,1,1,1,1))
#ifdef HAS_MPI
        if (dataset%is_parallel) then
          if (dataset%time_dim%size == NF90_UNLIMITED) then
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_COLLECTIVE)
          else
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_INDEPENDENT)
          end if
          call handle_error(ierr, "Failed to set parallel access for variable " // trim(var%name) // "!", __FILE__, __LINE__)
        end if
#endif
      end if
      do i = 1, dataset%group_nproc
        if (dataset%is_group_root .or. dataset%group_proc_id == i - 1) then
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          if (dataset%is_group_root) then
            call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          end if
          if (dataset%is_group_root) then
            if (i == 1) then
              buffer%r8(buffer%is:buffer%ie) = reshape(array, [size(array)])
            else
#ifdef HAS_MPI
              call MPI_IRECV(buffer%r8(buffer%is:buffer%ie), buffer%ie-buffer%is+1, MPI_DOUBLE, i-1, 88, &
                             dataset%group_mpi_comm, dataset%group_recv_req(i), ierr)
#endif
            end if
          else
#ifdef HAS_MPI
            call MPI_SEND(array, size(array), MPI_DOUBLE, 0, 88, dataset%group_mpi_comm, ierr)
#endif
          end if
        end if
      end do
      ! Write data into file.
      call buffer%shift_index_back()
      if (dataset%is_group_root) then
        do i = 1, dataset%group_nproc
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          if (i > 1) call dataset%wait_recv(i)
          if (var%has_unlimited_dim) then
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%r8(buffer%is:buffer%ie), &
              start=[dataset%group_start(1:ndim,i),dataset%time_step], &
              count=[dataset%group_count(1:ndim,i),1])
          else
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%r8(buffer%is:buffer%ie), &
              start=dataset%group_start(1:ndim,i), &
              count=dataset%group_count(1:ndim,i))
          end if
          if (fatal_opt) then
            call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                              trim(dataset%file_path) // """!", __FILE__, __LINE__)
          end if
        end do
      end if
      call buffer%clear()
    end if
    ! if (dataset%proc_id == 0) then
    !   time2 = MPI_WTIME()
    !   print *, "   - write cost ", time2 - time1
    ! end if

  end subroutine fiona_output_5d_r8

  subroutine fiona_output_6d_i2(dataset_name, var_name, array, start, count, fatal, only_root)

    integer, parameter :: ndim = 6

    character(*), intent(in) :: dataset_name
    character(*), intent(in) :: var_name
    integer(2), intent(in) :: array(:,:,:,:,:,:)
    integer, intent(in), optional :: start(ndim)
    integer, intent(in), optional :: count(ndim)
    logical, intent(in), optional :: fatal
    logical, intent(in), optional :: only_root

    type(dataset_type), pointer :: dataset
    type(var_type), pointer :: var
    type(buffer_type) buffer
    logical fatal_opt
    logical only_root_opt
    integer i, ierr
    real(8) time1, time2

    fatal_opt = .false.; if (present(fatal)) fatal_opt = fatal
    only_root_opt = .false.; if (present(only_root)) only_root_opt = only_root

    dataset => get_dataset(dataset_name)
    ! if (dataset%proc_id == 0) then
    !   time1 = MPI_WTIME()
    !   print *, " - output " // trim(var_name) // ":"
    ! end if
    var => dataset%get_var(var_name)
    if (only_root_opt) then
      ierr =  NF90_PUT_VAR(dataset%id, var%id, array)
      if (fatal_opt) then
        call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                          trim(dataset%file_path) // """!", __FILE__, __LINE__)
      end if
    else
      call gather_start_count(dataset, ndim, shape(array), start, count)
      if (dataset%is_group_root) then
        call buffer%init(buffer_size(dataset, ndim), array(1,1,1,1,1,1))
#ifdef HAS_MPI
        if (dataset%is_parallel) then
          if (dataset%time_dim%size == NF90_UNLIMITED) then
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_COLLECTIVE)
          else
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_INDEPENDENT)
          end if
          call handle_error(ierr, "Failed to set parallel access for variable " // trim(var%name) // "!", __FILE__, __LINE__)
        end if
#endif
      end if
      do i = 1, dataset%group_nproc
        if (dataset%is_group_root .or. dataset%group_proc_id == i - 1) then
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          if (dataset%is_group_root) then
            call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          end if
          if (dataset%is_group_root) then
            if (i == 1) then
              buffer%i2(buffer%is:buffer%ie) = reshape(array, [size(array)])
            else
#ifdef HAS_MPI
              call MPI_IRECV(buffer%i2(buffer%is:buffer%ie), buffer%ie-buffer%is+1, MPI_SHORT, i-1, 88, &
                             dataset%group_mpi_comm, dataset%group_recv_req(i), ierr)
#endif
            end if
          else
#ifdef HAS_MPI
            call MPI_SEND(array, size(array), MPI_SHORT, 0, 88, dataset%group_mpi_comm, ierr)
#endif
          end if
        end if
      end do
      ! Write data into file.
      call buffer%shift_index_back()
      if (dataset%is_group_root) then
        do i = 1, dataset%group_nproc
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          if (i > 1) call dataset%wait_recv(i)
          if (var%has_unlimited_dim) then
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%i2(buffer%is:buffer%ie), &
              start=[dataset%group_start(1:ndim,i),dataset%time_step], &
              count=[dataset%group_count(1:ndim,i),1])
          else
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%i2(buffer%is:buffer%ie), &
              start=dataset%group_start(1:ndim,i), &
              count=dataset%group_count(1:ndim,i))
          end if
          if (fatal_opt) then
            call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                              trim(dataset%file_path) // """!", __FILE__, __LINE__)
          end if
        end do
      end if
      call buffer%clear()
    end if
    ! if (dataset%proc_id == 0) then
    !   time2 = MPI_WTIME()
    !   print *, "   - write cost ", time2 - time1
    ! end if

  end subroutine fiona_output_6d_i2

  subroutine fiona_output_6d_i4(dataset_name, var_name, array, start, count, fatal, only_root)

    integer, parameter :: ndim = 6

    character(*), intent(in) :: dataset_name
    character(*), intent(in) :: var_name
    integer(4), intent(in) :: array(:,:,:,:,:,:)
    integer, intent(in), optional :: start(ndim)
    integer, intent(in), optional :: count(ndim)
    logical, intent(in), optional :: fatal
    logical, intent(in), optional :: only_root

    type(dataset_type), pointer :: dataset
    type(var_type), pointer :: var
    type(buffer_type) buffer
    logical fatal_opt
    logical only_root_opt
    integer i, ierr
    real(8) time1, time2

    fatal_opt = .false.; if (present(fatal)) fatal_opt = fatal
    only_root_opt = .false.; if (present(only_root)) only_root_opt = only_root

    dataset => get_dataset(dataset_name)
    ! if (dataset%proc_id == 0) then
    !   time1 = MPI_WTIME()
    !   print *, " - output " // trim(var_name) // ":"
    ! end if
    var => dataset%get_var(var_name)
    if (only_root_opt) then
      ierr =  NF90_PUT_VAR(dataset%id, var%id, array)
      if (fatal_opt) then
        call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                          trim(dataset%file_path) // """!", __FILE__, __LINE__)
      end if
    else
      call gather_start_count(dataset, ndim, shape(array), start, count)
      if (dataset%is_group_root) then
        call buffer%init(buffer_size(dataset, ndim), array(1,1,1,1,1,1))
#ifdef HAS_MPI
        if (dataset%is_parallel) then
          if (dataset%time_dim%size == NF90_UNLIMITED) then
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_COLLECTIVE)
          else
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_INDEPENDENT)
          end if
          call handle_error(ierr, "Failed to set parallel access for variable " // trim(var%name) // "!", __FILE__, __LINE__)
        end if
#endif
      end if
      do i = 1, dataset%group_nproc
        if (dataset%is_group_root .or. dataset%group_proc_id == i - 1) then
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          if (dataset%is_group_root) then
            call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          end if
          if (dataset%is_group_root) then
            if (i == 1) then
              buffer%i4(buffer%is:buffer%ie) = reshape(array, [size(array)])
            else
#ifdef HAS_MPI
              call MPI_IRECV(buffer%i4(buffer%is:buffer%ie), buffer%ie-buffer%is+1, MPI_INT, i-1, 88, &
                             dataset%group_mpi_comm, dataset%group_recv_req(i), ierr)
#endif
            end if
          else
#ifdef HAS_MPI
            call MPI_SEND(array, size(array), MPI_INT, 0, 88, dataset%group_mpi_comm, ierr)
#endif
          end if
        end if
      end do
      ! Write data into file.
      call buffer%shift_index_back()
      if (dataset%is_group_root) then
        do i = 1, dataset%group_nproc
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          if (i > 1) call dataset%wait_recv(i)
          if (var%has_unlimited_dim) then
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%i4(buffer%is:buffer%ie), &
              start=[dataset%group_start(1:ndim,i),dataset%time_step], &
              count=[dataset%group_count(1:ndim,i),1])
          else
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%i4(buffer%is:buffer%ie), &
              start=dataset%group_start(1:ndim,i), &
              count=dataset%group_count(1:ndim,i))
          end if
          if (fatal_opt) then
            call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                              trim(dataset%file_path) // """!", __FILE__, __LINE__)
          end if
        end do
      end if
      call buffer%clear()
    end if
    ! if (dataset%proc_id == 0) then
    !   time2 = MPI_WTIME()
    !   print *, "   - write cost ", time2 - time1
    ! end if

  end subroutine fiona_output_6d_i4

  subroutine fiona_output_6d_r4(dataset_name, var_name, array, start, count, fatal, only_root)

    integer, parameter :: ndim = 6

    character(*), intent(in) :: dataset_name
    character(*), intent(in) :: var_name
    real(4), intent(in) :: array(:,:,:,:,:,:)
    integer, intent(in), optional :: start(ndim)
    integer, intent(in), optional :: count(ndim)
    logical, intent(in), optional :: fatal
    logical, intent(in), optional :: only_root

    type(dataset_type), pointer :: dataset
    type(var_type), pointer :: var
    type(buffer_type) buffer
    logical fatal_opt
    logical only_root_opt
    integer i, ierr
    real(8) time1, time2

    fatal_opt = .false.; if (present(fatal)) fatal_opt = fatal
    only_root_opt = .false.; if (present(only_root)) only_root_opt = only_root

    dataset => get_dataset(dataset_name)
    ! if (dataset%proc_id == 0) then
    !   time1 = MPI_WTIME()
    !   print *, " - output " // trim(var_name) // ":"
    ! end if
    var => dataset%get_var(var_name)
    if (only_root_opt) then
      ierr =  NF90_PUT_VAR(dataset%id, var%id, array)
      if (fatal_opt) then
        call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                          trim(dataset%file_path) // """!", __FILE__, __LINE__)
      end if
    else
      call gather_start_count(dataset, ndim, shape(array), start, count)
      if (dataset%is_group_root) then
        call buffer%init(buffer_size(dataset, ndim), array(1,1,1,1,1,1))
#ifdef HAS_MPI
        if (dataset%is_parallel) then
          if (dataset%time_dim%size == NF90_UNLIMITED) then
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_COLLECTIVE)
          else
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_INDEPENDENT)
          end if
          call handle_error(ierr, "Failed to set parallel access for variable " // trim(var%name) // "!", __FILE__, __LINE__)
        end if
#endif
      end if
      do i = 1, dataset%group_nproc
        if (dataset%is_group_root .or. dataset%group_proc_id == i - 1) then
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          if (dataset%is_group_root) then
            call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          end if
          if (dataset%is_group_root) then
            if (i == 1) then
              buffer%r4(buffer%is:buffer%ie) = reshape(array, [size(array)])
            else
#ifdef HAS_MPI
              call MPI_IRECV(buffer%r4(buffer%is:buffer%ie), buffer%ie-buffer%is+1, MPI_FLOAT, i-1, 88, &
                             dataset%group_mpi_comm, dataset%group_recv_req(i), ierr)
#endif
            end if
          else
#ifdef HAS_MPI
            call MPI_SEND(array, size(array), MPI_FLOAT, 0, 88, dataset%group_mpi_comm, ierr)
#endif
          end if
        end if
      end do
      ! Write data into file.
      call buffer%shift_index_back()
      if (dataset%is_group_root) then
        do i = 1, dataset%group_nproc
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          if (i > 1) call dataset%wait_recv(i)
          if (var%has_unlimited_dim) then
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%r4(buffer%is:buffer%ie), &
              start=[dataset%group_start(1:ndim,i),dataset%time_step], &
              count=[dataset%group_count(1:ndim,i),1])
          else
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%r4(buffer%is:buffer%ie), &
              start=dataset%group_start(1:ndim,i), &
              count=dataset%group_count(1:ndim,i))
          end if
          if (fatal_opt) then
            call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                              trim(dataset%file_path) // """!", __FILE__, __LINE__)
          end if
        end do
      end if
      call buffer%clear()
    end if
    ! if (dataset%proc_id == 0) then
    !   time2 = MPI_WTIME()
    !   print *, "   - write cost ", time2 - time1
    ! end if

  end subroutine fiona_output_6d_r4

  subroutine fiona_output_6d_r8(dataset_name, var_name, array, start, count, fatal, only_root)

    integer, parameter :: ndim = 6

    character(*), intent(in) :: dataset_name
    character(*), intent(in) :: var_name
    real(8), intent(in) :: array(:,:,:,:,:,:)
    integer, intent(in), optional :: start(ndim)
    integer, intent(in), optional :: count(ndim)
    logical, intent(in), optional :: fatal
    logical, intent(in), optional :: only_root

    type(dataset_type), pointer :: dataset
    type(var_type), pointer :: var
    type(buffer_type) buffer
    logical fatal_opt
    logical only_root_opt
    integer i, ierr
    real(8) time1, time2

    fatal_opt = .false.; if (present(fatal)) fatal_opt = fatal
    only_root_opt = .false.; if (present(only_root)) only_root_opt = only_root

    dataset => get_dataset(dataset_name)
    ! if (dataset%proc_id == 0) then
    !   time1 = MPI_WTIME()
    !   print *, " - output " // trim(var_name) // ":"
    ! end if
    var => dataset%get_var(var_name)
    if (only_root_opt) then
      ierr =  NF90_PUT_VAR(dataset%id, var%id, array)
      if (fatal_opt) then
        call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                          trim(dataset%file_path) // """!", __FILE__, __LINE__)
      end if
    else
      call gather_start_count(dataset, ndim, shape(array), start, count)
      if (dataset%is_group_root) then
        call buffer%init(buffer_size(dataset, ndim), array(1,1,1,1,1,1))
#ifdef HAS_MPI
        if (dataset%is_parallel) then
          if (dataset%time_dim%size == NF90_UNLIMITED) then
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_COLLECTIVE)
          else
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_INDEPENDENT)
          end if
          call handle_error(ierr, "Failed to set parallel access for variable " // trim(var%name) // "!", __FILE__, __LINE__)
        end if
#endif
      end if
      do i = 1, dataset%group_nproc
        if (dataset%is_group_root .or. dataset%group_proc_id == i - 1) then
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          if (dataset%is_group_root) then
            call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          end if
          if (dataset%is_group_root) then
            if (i == 1) then
              buffer%r8(buffer%is:buffer%ie) = reshape(array, [size(array)])
            else
#ifdef HAS_MPI
              call MPI_IRECV(buffer%r8(buffer%is:buffer%ie), buffer%ie-buffer%is+1, MPI_DOUBLE, i-1, 88, &
                             dataset%group_mpi_comm, dataset%group_recv_req(i), ierr)
#endif
            end if
          else
#ifdef HAS_MPI
            call MPI_SEND(array, size(array), MPI_DOUBLE, 0, 88, dataset%group_mpi_comm, ierr)
#endif
          end if
        end if
      end do
      ! Write data into file.
      call buffer%shift_index_back()
      if (dataset%is_group_root) then
        do i = 1, dataset%group_nproc
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          if (i > 1) call dataset%wait_recv(i)
          if (var%has_unlimited_dim) then
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%r8(buffer%is:buffer%ie), &
              start=[dataset%group_start(1:ndim,i),dataset%time_step], &
              count=[dataset%group_count(1:ndim,i),1])
          else
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%r8(buffer%is:buffer%ie), &
              start=dataset%group_start(1:ndim,i), &
              count=dataset%group_count(1:ndim,i))
          end if
          if (fatal_opt) then
            call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                              trim(dataset%file_path) // """!", __FILE__, __LINE__)
          end if
        end do
      end if
      call buffer%clear()
    end if
    ! if (dataset%proc_id == 0) then
    !   time2 = MPI_WTIME()
    !   print *, "   - write cost ", time2 - time1
    ! end if

  end subroutine fiona_output_6d_r8

  subroutine fiona_output_7d_i2(dataset_name, var_name, array, start, count, fatal, only_root)

    integer, parameter :: ndim = 7

    character(*), intent(in) :: dataset_name
    character(*), intent(in) :: var_name
    integer(2), intent(in) :: array(:,:,:,:,:,:,:)
    integer, intent(in), optional :: start(ndim)
    integer, intent(in), optional :: count(ndim)
    logical, intent(in), optional :: fatal
    logical, intent(in), optional :: only_root

    type(dataset_type), pointer :: dataset
    type(var_type), pointer :: var
    type(buffer_type) buffer
    logical fatal_opt
    logical only_root_opt
    integer i, ierr
    real(8) time1, time2

    fatal_opt = .false.; if (present(fatal)) fatal_opt = fatal
    only_root_opt = .false.; if (present(only_root)) only_root_opt = only_root

    dataset => get_dataset(dataset_name)
    ! if (dataset%proc_id == 0) then
    !   time1 = MPI_WTIME()
    !   print *, " - output " // trim(var_name) // ":"
    ! end if
    var => dataset%get_var(var_name)
    if (only_root_opt) then
      ierr =  NF90_PUT_VAR(dataset%id, var%id, array)
      if (fatal_opt) then
        call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                          trim(dataset%file_path) // """!", __FILE__, __LINE__)
      end if
    else
      call gather_start_count(dataset, ndim, shape(array), start, count)
      if (dataset%is_group_root) then
        call buffer%init(buffer_size(dataset, ndim), array(1,1,1,1,1,1,1))
#ifdef HAS_MPI
        if (dataset%is_parallel) then
          if (dataset%time_dim%size == NF90_UNLIMITED) then
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_COLLECTIVE)
          else
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_INDEPENDENT)
          end if
          call handle_error(ierr, "Failed to set parallel access for variable " // trim(var%name) // "!", __FILE__, __LINE__)
        end if
#endif
      end if
      do i = 1, dataset%group_nproc
        if (dataset%is_group_root .or. dataset%group_proc_id == i - 1) then
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          if (dataset%is_group_root) then
            call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          end if
          if (dataset%is_group_root) then
            if (i == 1) then
              buffer%i2(buffer%is:buffer%ie) = reshape(array, [size(array)])
            else
#ifdef HAS_MPI
              call MPI_IRECV(buffer%i2(buffer%is:buffer%ie), buffer%ie-buffer%is+1, MPI_SHORT, i-1, 88, &
                             dataset%group_mpi_comm, dataset%group_recv_req(i), ierr)
#endif
            end if
          else
#ifdef HAS_MPI
            call MPI_SEND(array, size(array), MPI_SHORT, 0, 88, dataset%group_mpi_comm, ierr)
#endif
          end if
        end if
      end do
      ! Write data into file.
      call buffer%shift_index_back()
      if (dataset%is_group_root) then
        do i = 1, dataset%group_nproc
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          if (i > 1) call dataset%wait_recv(i)
          if (var%has_unlimited_dim) then
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%i2(buffer%is:buffer%ie), &
              start=[dataset%group_start(1:ndim,i),dataset%time_step], &
              count=[dataset%group_count(1:ndim,i),1])
          else
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%i2(buffer%is:buffer%ie), &
              start=dataset%group_start(1:ndim,i), &
              count=dataset%group_count(1:ndim,i))
          end if
          if (fatal_opt) then
            call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                              trim(dataset%file_path) // """!", __FILE__, __LINE__)
          end if
        end do
      end if
      call buffer%clear()
    end if
    ! if (dataset%proc_id == 0) then
    !   time2 = MPI_WTIME()
    !   print *, "   - write cost ", time2 - time1
    ! end if

  end subroutine fiona_output_7d_i2

  subroutine fiona_output_7d_i4(dataset_name, var_name, array, start, count, fatal, only_root)

    integer, parameter :: ndim = 7

    character(*), intent(in) :: dataset_name
    character(*), intent(in) :: var_name
    integer(4), intent(in) :: array(:,:,:,:,:,:,:)
    integer, intent(in), optional :: start(ndim)
    integer, intent(in), optional :: count(ndim)
    logical, intent(in), optional :: fatal
    logical, intent(in), optional :: only_root

    type(dataset_type), pointer :: dataset
    type(var_type), pointer :: var
    type(buffer_type) buffer
    logical fatal_opt
    logical only_root_opt
    integer i, ierr
    real(8) time1, time2

    fatal_opt = .false.; if (present(fatal)) fatal_opt = fatal
    only_root_opt = .false.; if (present(only_root)) only_root_opt = only_root

    dataset => get_dataset(dataset_name)
    ! if (dataset%proc_id == 0) then
    !   time1 = MPI_WTIME()
    !   print *, " - output " // trim(var_name) // ":"
    ! end if
    var => dataset%get_var(var_name)
    if (only_root_opt) then
      ierr =  NF90_PUT_VAR(dataset%id, var%id, array)
      if (fatal_opt) then
        call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                          trim(dataset%file_path) // """!", __FILE__, __LINE__)
      end if
    else
      call gather_start_count(dataset, ndim, shape(array), start, count)
      if (dataset%is_group_root) then
        call buffer%init(buffer_size(dataset, ndim), array(1,1,1,1,1,1,1))
#ifdef HAS_MPI
        if (dataset%is_parallel) then
          if (dataset%time_dim%size == NF90_UNLIMITED) then
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_COLLECTIVE)
          else
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_INDEPENDENT)
          end if
          call handle_error(ierr, "Failed to set parallel access for variable " // trim(var%name) // "!", __FILE__, __LINE__)
        end if
#endif
      end if
      do i = 1, dataset%group_nproc
        if (dataset%is_group_root .or. dataset%group_proc_id == i - 1) then
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          if (dataset%is_group_root) then
            call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          end if
          if (dataset%is_group_root) then
            if (i == 1) then
              buffer%i4(buffer%is:buffer%ie) = reshape(array, [size(array)])
            else
#ifdef HAS_MPI
              call MPI_IRECV(buffer%i4(buffer%is:buffer%ie), buffer%ie-buffer%is+1, MPI_INT, i-1, 88, &
                             dataset%group_mpi_comm, dataset%group_recv_req(i), ierr)
#endif
            end if
          else
#ifdef HAS_MPI
            call MPI_SEND(array, size(array), MPI_INT, 0, 88, dataset%group_mpi_comm, ierr)
#endif
          end if
        end if
      end do
      ! Write data into file.
      call buffer%shift_index_back()
      if (dataset%is_group_root) then
        do i = 1, dataset%group_nproc
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          if (i > 1) call dataset%wait_recv(i)
          if (var%has_unlimited_dim) then
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%i4(buffer%is:buffer%ie), &
              start=[dataset%group_start(1:ndim,i),dataset%time_step], &
              count=[dataset%group_count(1:ndim,i),1])
          else
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%i4(buffer%is:buffer%ie), &
              start=dataset%group_start(1:ndim,i), &
              count=dataset%group_count(1:ndim,i))
          end if
          if (fatal_opt) then
            call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                              trim(dataset%file_path) // """!", __FILE__, __LINE__)
          end if
        end do
      end if
      call buffer%clear()
    end if
    ! if (dataset%proc_id == 0) then
    !   time2 = MPI_WTIME()
    !   print *, "   - write cost ", time2 - time1
    ! end if

  end subroutine fiona_output_7d_i4

  subroutine fiona_output_7d_r4(dataset_name, var_name, array, start, count, fatal, only_root)

    integer, parameter :: ndim = 7

    character(*), intent(in) :: dataset_name
    character(*), intent(in) :: var_name
    real(4), intent(in) :: array(:,:,:,:,:,:,:)
    integer, intent(in), optional :: start(ndim)
    integer, intent(in), optional :: count(ndim)
    logical, intent(in), optional :: fatal
    logical, intent(in), optional :: only_root

    type(dataset_type), pointer :: dataset
    type(var_type), pointer :: var
    type(buffer_type) buffer
    logical fatal_opt
    logical only_root_opt
    integer i, ierr
    real(8) time1, time2

    fatal_opt = .false.; if (present(fatal)) fatal_opt = fatal
    only_root_opt = .false.; if (present(only_root)) only_root_opt = only_root

    dataset => get_dataset(dataset_name)
    ! if (dataset%proc_id == 0) then
    !   time1 = MPI_WTIME()
    !   print *, " - output " // trim(var_name) // ":"
    ! end if
    var => dataset%get_var(var_name)
    if (only_root_opt) then
      ierr =  NF90_PUT_VAR(dataset%id, var%id, array)
      if (fatal_opt) then
        call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                          trim(dataset%file_path) // """!", __FILE__, __LINE__)
      end if
    else
      call gather_start_count(dataset, ndim, shape(array), start, count)
      if (dataset%is_group_root) then
        call buffer%init(buffer_size(dataset, ndim), array(1,1,1,1,1,1,1))
#ifdef HAS_MPI
        if (dataset%is_parallel) then
          if (dataset%time_dim%size == NF90_UNLIMITED) then
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_COLLECTIVE)
          else
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_INDEPENDENT)
          end if
          call handle_error(ierr, "Failed to set parallel access for variable " // trim(var%name) // "!", __FILE__, __LINE__)
        end if
#endif
      end if
      do i = 1, dataset%group_nproc
        if (dataset%is_group_root .or. dataset%group_proc_id == i - 1) then
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          if (dataset%is_group_root) then
            call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          end if
          if (dataset%is_group_root) then
            if (i == 1) then
              buffer%r4(buffer%is:buffer%ie) = reshape(array, [size(array)])
            else
#ifdef HAS_MPI
              call MPI_IRECV(buffer%r4(buffer%is:buffer%ie), buffer%ie-buffer%is+1, MPI_FLOAT, i-1, 88, &
                             dataset%group_mpi_comm, dataset%group_recv_req(i), ierr)
#endif
            end if
          else
#ifdef HAS_MPI
            call MPI_SEND(array, size(array), MPI_FLOAT, 0, 88, dataset%group_mpi_comm, ierr)
#endif
          end if
        end if
      end do
      ! Write data into file.
      call buffer%shift_index_back()
      if (dataset%is_group_root) then
        do i = 1, dataset%group_nproc
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          if (i > 1) call dataset%wait_recv(i)
          if (var%has_unlimited_dim) then
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%r4(buffer%is:buffer%ie), &
              start=[dataset%group_start(1:ndim,i),dataset%time_step], &
              count=[dataset%group_count(1:ndim,i),1])
          else
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%r4(buffer%is:buffer%ie), &
              start=dataset%group_start(1:ndim,i), &
              count=dataset%group_count(1:ndim,i))
          end if
          if (fatal_opt) then
            call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                              trim(dataset%file_path) // """!", __FILE__, __LINE__)
          end if
        end do
      end if
      call buffer%clear()
    end if
    ! if (dataset%proc_id == 0) then
    !   time2 = MPI_WTIME()
    !   print *, "   - write cost ", time2 - time1
    ! end if

  end subroutine fiona_output_7d_r4

  subroutine fiona_output_7d_r8(dataset_name, var_name, array, start, count, fatal, only_root)

    integer, parameter :: ndim = 7

    character(*), intent(in) :: dataset_name
    character(*), intent(in) :: var_name
    real(8), intent(in) :: array(:,:,:,:,:,:,:)
    integer, intent(in), optional :: start(ndim)
    integer, intent(in), optional :: count(ndim)
    logical, intent(in), optional :: fatal
    logical, intent(in), optional :: only_root

    type(dataset_type), pointer :: dataset
    type(var_type), pointer :: var
    type(buffer_type) buffer
    logical fatal_opt
    logical only_root_opt
    integer i, ierr
    real(8) time1, time2

    fatal_opt = .false.; if (present(fatal)) fatal_opt = fatal
    only_root_opt = .false.; if (present(only_root)) only_root_opt = only_root

    dataset => get_dataset(dataset_name)
    ! if (dataset%proc_id == 0) then
    !   time1 = MPI_WTIME()
    !   print *, " - output " // trim(var_name) // ":"
    ! end if
    var => dataset%get_var(var_name)
    if (only_root_opt) then
      ierr =  NF90_PUT_VAR(dataset%id, var%id, array)
      if (fatal_opt) then
        call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                          trim(dataset%file_path) // """!", __FILE__, __LINE__)
      end if
    else
      call gather_start_count(dataset, ndim, shape(array), start, count)
      if (dataset%is_group_root) then
        call buffer%init(buffer_size(dataset, ndim), array(1,1,1,1,1,1,1))
#ifdef HAS_MPI
        if (dataset%is_parallel) then
          if (dataset%time_dim%size == NF90_UNLIMITED) then
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_COLLECTIVE)
          else
            ierr = NF90_VAR_PAR_ACCESS(dataset%id, var%id, NF90_INDEPENDENT)
          end if
          call handle_error(ierr, "Failed to set parallel access for variable " // trim(var%name) // "!", __FILE__, __LINE__)
        end if
#endif
      end if
      do i = 1, dataset%group_nproc
        if (dataset%is_group_root .or. dataset%group_proc_id == i - 1) then
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          if (dataset%is_group_root) then
            call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          end if
          if (dataset%is_group_root) then
            if (i == 1) then
              buffer%r8(buffer%is:buffer%ie) = reshape(array, [size(array)])
            else
#ifdef HAS_MPI
              call MPI_IRECV(buffer%r8(buffer%is:buffer%ie), buffer%ie-buffer%is+1, MPI_DOUBLE, i-1, 88, &
                             dataset%group_mpi_comm, dataset%group_recv_req(i), ierr)
#endif
            end if
          else
#ifdef HAS_MPI
            call MPI_SEND(array, size(array), MPI_DOUBLE, 0, 88, dataset%group_mpi_comm, ierr)
#endif
          end if
        end if
      end do
      ! Write data into file.
      call buffer%shift_index_back()
      if (dataset%is_group_root) then
        do i = 1, dataset%group_nproc
          if (product(dataset%group_count(1:ndim,i)) == 0) cycle
          call buffer%shift_index(product(dataset%group_count(1:ndim,i)))
          if (i > 1) call dataset%wait_recv(i)
          if (var%has_unlimited_dim) then
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%r8(buffer%is:buffer%ie), &
              start=[dataset%group_start(1:ndim,i),dataset%time_step], &
              count=[dataset%group_count(1:ndim,i),1])
          else
            ierr = NF90_PUT_VAR(dataset%id, var%id, buffer%r8(buffer%is:buffer%ie), &
              start=dataset%group_start(1:ndim,i), &
              count=dataset%group_count(1:ndim,i))
          end if
          if (fatal_opt) then
            call handle_error(ierr, "Failed to write variable """ // trim(var%name) // """ to dataset """ // &
                              trim(dataset%file_path) // """!", __FILE__, __LINE__)
          end if
        end do
      end if
      call buffer%clear()
    end if
    ! if (dataset%proc_id == 0) then
    !   time2 = MPI_WTIME()
    !   print *, "   - write cost ", time2 - time1
    ! end if

  end subroutine fiona_output_7d_r8

